Prologue

It took me three months to finish writing this article. I had so many tasks on my to-do list that sadly this one was pushed down to the bottom of the list. Last weekend I made a promise to myself that until Sunday I’m going to finish writing it, I successfully kept my word and here it is, another radare2 tutorial.

Today we’ll solve a very nice challenge, “packedup”, written by ad3l for r2con 2017 competition. It is not the first writeup that I publish from r2con competition, you can check out “Reverse engineering a Gameboy ROM with radare2” as well, make sure not to miss the cool swags I got from winning the competition.

This article is aimed to those of you who are familiar with radare2. If you are not, I suggest you to start from part 1 of my series “A Journey Into Radare2”.

So, without further ado, let’s dig into the binary.

Getting radare2

Installation

Radare2’s development is pretty quick – the project evolves every day, therefore it’s recommended to use the current git version over the stable one. Sometimes the stable version is less stable than the current git version!

$ git clone https://github.com/radare/radare2.git $ cd radare2 $ ./sys/install.sh

If you don’t want to install the git version or you want the binaries for another machine (Windows, OS X, iOS, etc.) check out the download page at the radare2 website.

Updating

As I said before, it is highly recommended to always use the newest version of r2 from the git repository. All you need to do to update your r2 version from the git is to execute:

$ ./sys/install.sh

And you’ll have the latest version from git. I usually update my version of radare2 in the morning with a scheduled task, so I can wake up to the latest version available. If you’re using radare2 often, I recommend you do the same.

packedup

You can download packedup from here. I suggest you to star (★) the repository to get updates about more radare2 tutorials 🙂

First thing to do, obviously, is to execute the binary and get a basic feeling of what we are going to face.

$ ./packedup Welcome to packedup for r2crackmes :) Flag << MEGABEETS Try again!

packedup is executed, it requests us to give it a flag. It then probably does some calculations at the backend to see if the inputted flag is the right one. I entered “MEGABEETS” which is likely not the correct flag and finished with the fail message — “Try again!”.

Reversing time!

Now for our favorite part, let’s open the binary with radare2 and try to figure out how packedup is checking the submitted flag:

$ r2 ./packedup

— Here be dragons.

[0x004004d0]> aaa

[x] Analyze all flags starting with sym. and entry0 (aa)

[x] Analyze len bytes of instructions for references (aar)

[x] Analyze function calls (aac)

[*] Use -AA or aaaa to perform additional experimental analysis.

[x] Constructing a function name for fcn.* and sym.func.* functions (aan)

Analysis

I usually begin with executing aa (analyze all) or with aas (to analyze functions, symbols and more). The name is misleading because there is a lot more to analyze (check aa? ) but it’s enough to start with for most of the binaries I examined. This time we’ll start straight with aaa to make things simpler and due to the binary’s small size. You can also run radare2 with the -A flag to analyze the binary straight at startup using aaa (e.g r2 -A ./packedup ).

Note: as I mentioned in the previous posts, starting with aaa is not always the recommended approach since analysis is very complicated process. I wrote more about it in this answer — read it to better understand why.

Getting Information

So now that we opened our binary with radare2, we have been located automatically at the program’s entrypoint. But before we start working on the code itself It’s a good approach to get to know our binary characteristics. radare2 can show us the information we need using the i command (I removed some information for the sake of readability):

[0x004004d0]> i ... file ./packedup format elf64 iorw false mode -r-x size 0x1878 humansz 6.1K type EXEC (Executable file) arch x86 ... bintype elf bits 64 ... endian little ... intrp /lib64/ld-linux-x86-64.so.2 lang c ... machine AMD x86-64 architecture stripped true ...

The i command used for getting info about the opened file. It’s a wrapper around rabin2 which is an information extractor tool in the radare2 framework. radare2 offers us tons amount of information about the binary. Check out i? to list the information’s subcommands.

packedup is a 64-bit stripped ELF binary. Cool. Let’s move on.

Strings

One of my favorite ways to figure out where to start my analysis is to check the binary for indicative strings, and especially where they’re referenced from. To list all the strings in the data section we’ll use iz :

[0x004004d0]> iz

001 0x000007d0 0x004007d0 46 47 (.rodata) ascii Welcome to packedup for r2crackmes :)

Flag <<

002 0x00000800 0x00400800 11 12 (.rodata) ascii Try again!



003 0x0000080d 0x0040080d 26 27 (.rodata) ascii Yep! you got the flag 🙂



Nice! We have our “welcome” message and both the “Good-boy” and the “Bad-boy” messages. Let’s see where each is referenced from. To do this we’ll use a simple trick that radare2 has – built in loops:

[0x004004d0]> axt @@ str.*

main 0x400619 [data] mov esi, str.Welcome_to_packedup_for_r2crackmes_:___Flag

main 0x4006b8 [data] mov esi, str.Try_again

main 0x4006de [data] mov esi, str.Yep__you_got_the_flag_:

This command reveals us more of radare2 features. The axt command is used to “find data/code references to this address” (see ax? ).

@@ is like foreach iterator sign, used to repeat a command over a list of offsets (see @@? ), and str.* is a wildcard for all the flags that start with str. . This combination helps me not just to list the strings but also to list the function name, where they’re used and the referenced instruction.

Seeking

As I mentioned before, all this time we were at the entrypoint of the program, now it’s time to move on. The strings we just listed are all referenced by ‘main’. In order to navigate from offset to offset we need to use the ‘seek’ command, represented by s . As you already know, appending ? to (almost) every command is the answer to all your problems.

[0x08048370]> s?

| Usage: s # Seek commands

| s Print current address

| s addr Seek to address

| s- Undo seek

| s- n Seek n bytes backward

| s– Seek blocksize bytes backward

| s+ Redo seek

| s+ n Seek n bytes forward

| s++ Seek blocksize bytes forward

| s[j*=] List undo seek history (JSON, =list, *r2)

| s/ DATA Search for next occurrence of ‘DATA’

| s/x 9091 Search for next occurrence of \x90\x91

| s. hexoff Seek honoring a base from core->offset

| sa [[+-]a] [asz] Seek asz (or bsize) aligned to addr

| sb Seek aligned to bb start

| sC [?] string Seek to comment matching given string

| sf Seek to next function (f->addr+f->size)

| sf function Seek to address of specified function

| sg/sG Seek begin (sg) or end (sG) of section or file

| sl [?] [+-]line Seek to line

| sn/sp Seek next/prev scr.nkey

| so [N] Seek to N next opcode(s)

| sr pc Seek to register

So basically, the seek command accepts an address or math expression as an argument. The expression can be math operations, flag, or memory access operations. Since we want to seek to the main function, we don’t need anything special, we can do it by executing s main .

[0x004004d0]> s main

[0x0040060c]>

Disassembly

Now that we are at the main function we want to print its disassembly to examine it. There are many ways to disassemble a function using radare. You can use pdf (print disassembly function) or you can use more interactive ways – the Visual Mode ( v ) and the Visual Graph Mode ( VV ). You can use the way you find most comfortable to you. I usually use the visual modes since they’re more rich and interactive. Sadly, because this is not radare2 101 writeup I’m not going to explain how to use it, I explained it thoroughly in the previous articles.

Visual Graph Mode vs Visual Mode

Let’s start with having a look at the first block of main:

[0x0040060c]> pdf

╭ (fcn) main 244

│ main ();

│ ; var int local_10h @ rbp-0x10

│ ; var int local_ch @ rbp-0xc

│ ; var int local_8h @ rbp-0x8

│ ; JMP XREF from 0x00400605 (entry2.init)

│ ; DATA XREF from 0x004004ed (entry0)

│ 0x0040060c 55 push rbp

│ 0x0040060d 48 89 e5 mov rbp , rsp

│ 0x00400610 48 83 ec 10 sub rsp , 0x10

│ ; DATA XREF from 0x00400653 (main)

│ ; DATA XREF from 0x004005e7 (entry2.init)

│ 0x00400614 ba 30 00 00 00 mov edx , 0x30

│ 0x00400619 be d0 07 40 00 mov esi , str.Welcome_to_packedup_for_r2crackmes_:___Flag ; 0x4007d0 ; “Welcome to packedup for r2crackmes :)

Flag << “

│ 0x0040061e bf 01 00 00 00 mov edi , 1

│ 0x00400623 b8 00 00 00 00 mov eax , 0

│ 0x00400628 e8 53 fe ff ff call sym.imp.write ; ssize_t write(int fd, void *ptr, size_t nbytes)

│ 0x0040062d ba 2c 00 00 00 mov edx , 0x2c ; ‘,’ ; 44

│ 0x00400632 be 80 10 60 00 mov esi , 0x601080

│ 0x00400637 bf 00 00 00 00 mov edi , 0

│ 0x0040063c b8 00 00 00 00 mov eax , 0

│ 0x00400641 e8 4a fe ff ff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)

│ 0x00400646 48 98 cdqe

│ 0x00400648 48 89 45 f8 mov qword [ local_8h ], rax

│ 0x0040064c c7 45 f0 00 00 00 . mov dword [ local_10h ], 0

│ 0x00400653 b8 14 06 40 00 mov eax , 0x400614

│ 0x00400658 bb f6 06 40 00 mov ebx , 0x4006f6

│ 0x0040065d 29 c3 sub ebx , eax

│ 0x0040065f 31 c9 xor ecx , ecx

After the function’s prologue, we can see arguments passed (System V AMD64 ABI) to write() to print the “Welcome” message and then another arguments passed to read() to read stdin input. read() is reading 0x2c (Decimal: 44) bytes from stdin and stores them into a buffer at 0x601080. We can assume that the flag would probably be 44 characters long. Let’s rename the address of our input so it’ll be better for us to recognize this address in the disassembly:

[0x0040060c]> f loc.our_input = 0x601080



f is used to create flags, a “bookmark” to a certain address. For more information check f? and read the chapter about flags in radare2book.

Then the program stores the number of bytes read into a local variable. At 0x400653 the program is moving an address (0x400614) to eax and another address (0x4006f6) to ebx . The first address is the first instruction after main ‘s prologue and the second is the last instruction before main exits. So basically we can say that eax and ebx are storing the start and the end of main . Immediately after that eax is subtracted from ebx so now ebx will store the difference between these two addresses.

In the next block, the program uses the bytes of the code itself in a loop until it finishes to calculate some initial value.

…



│ 0x00400653 b8 14 06 40 00 mov eax , 0x400614

│ 0x00400658 bb f6 06 40 00 mov ebx , 0x4006f6

│ 0x0040065d 29 c3 sub ebx , eax

│ 0x0040065f 31 c9 xor ecx , ecx

│ ; JMP XREF from 0x0040066b (main)

│ ╭─> 0x00400661 67 02 08 add cl , byte [ eax ]

│ ⁝ 0x00400664 c1 c9 04 ror ecx , 4

│ ⁝ 0x00400667 ff c0 inc eax

│ ⁝ 0x00400669 ff cb dec ebx

│ ╰─< 0x0040066b 75 f4 jne 0x400661

│ 0x0040066d 89 ca mov edx , ecx



…

Now for a fun part. In the next loop we can see how to program is validating our input. First, let’s have a look at the most relevant part of the code:

│ 0x0040066d 89 ca mov edx , ecx

│ 0x0040066f 89 d0 mov eax , edx

│ 0x00400671 89 45 f0 mov dword [ local_10h ], eax

│ 0x00400674 c7 45 f4 2c 00 00 . mov dword [ local_ch ], 0x2c ; ‘,’ ; 44

│ ; JMP XREF from 0x004006f4 (main)

│ 0x0040067b 8b 45 f0 mov eax , dword [ local_10h ]

│ 0x0040067e 89 c0 mov eax , eax

│ 0x00400680 d1 c0 rol eax , 1

│ 0x00400682 89 45 f0 mov dword [ local_10h ], eax

│ 0x00400685 8b 45 f0 mov eax , dword [ local_10h ]

│ 0x00400688 0f b6 d0 movzx edx , al

│ 0x0040068b 8b 45 f4 mov eax , dword [ local_ch ]

│ 0x0040068e 83 e8 01 sub eax , 1

│ 0x00400691 48 98 cdqe

│ 0x00400693 0f b6 88 80 10 60 . movzx ecx , byte [ rax + 0x601080 ] ; [0x601080:1]=0

│ 0x0040069a 8b 45 f4 mov eax , dword [ local_ch ]

│ 0x0040069d 83 e8 01 sub eax , 1

│ 0x004006a0 48 98 cdqe

│ 0x004006a2 0f b6 80 a0 07 40 . movzx eax , byte [ rax + 0x4007a0 ] ; [0x4007a0:1]=15

│ 0x004006a9 31 c8 xor eax , ecx

│ 0x004006ab 0f b6 c0 movzx eax , al

│ 0x004006ae 39 c2 cmp edx , eax

│ ╭─< 0x004006b0 74 1c je 0x4006ce

The value that was calculated in the previous loop is now moved into edx (at 0x40066d), which in turn is moving it to eax which is moving it to [local_10h] . Confused? me too. To put things simple, the value that was calculated in the previous loop is now at [local_10h] . Now we are starting the validation loop. The first instruction of this loop moves the value of [local_10h] to eax , then, at 0x400680 the value at eax is being left-rotated with 1 byte and assigned again to [local_10h] . At 0x400688 the lower byte ( al ) of the value is moved into edx . At address 0x400693 the program takes one char from our input and assign it to ecx . After that, at 0x4006a2 the program takes one byte from [rax + 0x4007a0] and assigns it to eax . Let’s see what we have at 0x4007a0 :

[0x0040060c]> px 44 @ 0x4007a0

– offset – 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF

0x004007a0 0f c9 a8 86 ac e0 18 93 8a af 91 a2 e4 64 7a 5a . . . . . . . . . . . . . d z Z

0x004007b0 08 8b a8 9a b4 d1 1f 84 b4 d1 71 52 1c 8a 80 d2 . . . . . . . . . . q R . . . .

0x004007c0 14 95 82 97 80 d2 1f 90 8f 91 93 a6 . . . . . . . . . . . .

Cool! It is a predefined byte array of length 44. It is obviously been used somehow in the comparison. Let’s flag it to loc.predefined_array using:

[0x0040060c]> f loc.predefined_array = 0x4007a0

Just for a reminder before we continue. edx now contains the lower byte of rol(initial_value) , eax contains the byte in index 0x2C (Decimal: 44) of the byte array at loc.predefined_array and ecx holds the last character of our input.

At 0x4006a9, ecx (the last char of our input) is bitwise XOR’d with eax (last value of the predefined array), the result is stored in eax . Then al is zero-extended moved into eax and then eax is compared with edx (The lower byte of rol(initial_value) ). It will be easier to understand this operation using the following pseudo-python example:

key = 0x???????? # Our initial_value our_input = "A" * 44 predefined_array = [0x0fc9, ... ,0x93a6] if ( ord(our_input[-1]) ^ predefined_array[-1] ) == ( key & 0xff ): return True

If the result is indeed equal, i.e. ECX ^ EAX == EDX the loop is repeating itself with the next character from our input (2nd from the end), the next character from the predefined array (again, 2nd from the end) and with the result of rol(key) . key is actually the result of performing left-rotation on initial_value , since this value changed in every rotation it isn’t “initial” anymore so now we’ll call it key .

The solution: 1st try

Now that we understood the logic of the program we can move to find a solution for this crackme. The key insight from the previous paragraphs is the understanding that there is a loop that contains an equation with three variables: ECX ^ EAX = EDX . We know that both EAX and EDX are predictable since they should always stays the same. EAX is coming from the static-predefined byte-array and EDX is the result of n times key = rol(key) where n equals the number of the current iteration in our loop. The only variable that changes is EAX which is coming from our input.

So how can we know what input should we give to the program in order to make the equation true? We’ll use one of the coolest properties of Bitwise XOR — XOR is an involutory function (well, not exactly). Thus, and bear with me for a simple math, that means:

ECX ^ EAX == EDX EAX ^ EDX == ECX ECX ^ EDX == EAX

And that is awesome, right?! That’s mean that given ECX and EDX we can know what the value of EAX should be, in order to make the comparison return true. All we need to do is to write a python script and figure out what is the value of initial_value (i.e. EDX , key ). We’ll start from the python script and then we will figure out the initial value of the key.

First we’ll define a lambda function for rotate left:

# Rotate left lambda rol = lambda val, r_bits, max_bits: \ (val << r_bits%max_bits) & (2**max_bits-1) | \ ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))

Then, we’ll create our predefined array. We’ll use radare for this:

[0x0040060c]> pcp 44 @ 0x4007a0

import struct

buf = struct.pack (“44B”, *[

0x0f,0xc9,0xa8,0x86,0xac,0xe0,0x18,0x93,0x8a,0xaf,0x91,

0xa2,0xe4,0x64,0x7a,0x5a,0x08,0x8b,0xa8,0x9a,0xb4,0xd1,

0x1f,0x84,0xb4,0xd1,0x71,0x52,0x1c,0x8a,0x80,0xd2,0x14,

0x95,0x82,0x97,0x80,0xd2,0x1f,0x90,0x8f,0x91,0x93,0xa6])

The pc subcommands (check pc? ) is used to output the bytes in code formats of different languages as C, Python, Javascript and others.

We’ll add it to the script and define two more variables:

# Byte array from 0x004007A0, modified from the generated results of `pcp 44 @ 0x4007a0` arr =[0x0F, 0xC9, 0xA8, 0x86, 0xAC, 0xE0, 0x18, 0x93, 0x8A, 0xAF, 0x91, 0xA2, 0xE4, 0x64, 0x7A, 0x5A, 0x08, 0x8B, 0xA8, 0x9A, 0xB4, 0xD1, 0x1F, 0x84, 0xB4, 0xD1, 0x71, 0x52, 0x1C, 0x8A, 0x80, 0xD2, 0x14, 0x95, 0x82, 0x97, 0x80, 0xD2, 0x1F, 0x90, 0x8F, 0x91, 0x93, 0xA6] # Initial key value key = 0xdc77df87 flag = []

And the last thing is to implement the logic and combine it all together:

# Rotate left lambda rol = lambda val, r_bits, max_bits: \ (val << r_bits%max_bits) & (2**max_bits-1) | \ ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits))) # Byte array from 0x004007A0, modified from the generated results of `pcp 44 @ 0x4007a0` arr =[0x0F, 0xC9, 0xA8, 0x86, 0xAC, 0xE0, 0x18, 0x93, 0x8A, 0xAF, 0x91, 0xA2, 0xE4, 0x64, 0x7A, 0x5A, 0x08, 0x8B, 0xA8, 0x9A, 0xB4, 0xD1, 0x1F, 0x84, 0xB4, 0xD1, 0x71, 0x52, 0x1C, 0x8A, 0x80, 0xD2, 0x14, 0x95, 0x82, 0x97, 0x80, 0xD2, 0x1F, 0x90, 0x8F, 0x91, 0x93, 0xA6] # Initial key value key = 0x???????? flag = [] # Iterate the array backwards for b in reversed(arr): # rol the key key = rol(key,1,32) # xor the 8 lower bits of the key with current byte in array char = chr(b ^ (key & 0xff)) # Add char to final flag flag.insert(0, char) print '[+] Flag: ', ''.join(flag)

Awesome! Now we just need to find the initial value for the key. How do we do this? We should put a breakpoint after the loop which generates the initial value and check what is the result of this loop. Let’s open radare2 in debug mode r2 -d packedup or if your radare2 shell is still open you can use the ood command.

[0x0040060c]> ood

Process with PID 1236 started…

File dbg:///home/beet/Desktop/Security/r2con/crackmes/packedup reopened in read-write mode

= attach 1236 1236

[0x7fb45818cf30]>

ood is used to re-open the current file in debug mode. It is a subcommand of oo which responsible of handling opened files. There’s also an alias for ood which is doo . I usually use doo since it’s easier to remember and more fun to say 😉

As you can see, our seek address changed to 0x7fb45818cf30 which is an address in the memory map of the dynamic loader (ld.so). We can know it by simply execute dm. :

[0x7fb45818cf30]> dm.

/usr/lib/ld-2.26.so



dm. is used to show the map name of the current address. You can use dm to list the memory maps of the current process. You can see more subcommand by executing dm? .

We know that the end of the loop is at 0x0040066d , this is where the result of the loop, which is in ecx , is moving into edx . Let’s put a breakpoint on this address and check the value of ecx :

[0x7fb45818cf30]> db 0x40066d

[0x7fb45818cf30]> dc

Welcome to packedup for r2crackmes 🙂

Flag << Blah_blah_blah

hit breakpoint at: 40066d



[0x0040066d]> dr ecx

0xd477d83e



the d? subcommand is used for anything related to debug session. db stands for debug breakpoint and is used to define a breakpoint. Check db? for its subcommands. dc stands for debug continue and responsible to continue the execution. dr , debug registers, is used to display the value of a register. There are tons of subcommand to dr , check it out by executing dr?

Great! So now that we have the initial value of the key we can fill the missing part of the script key = 0xd477d83e and execute it:

beet:~$ python answer.py [+] Flag: Hj.xIgYj{u]J*l=ok\S6?TJbgh

What? This flag doesn’t make any sense. We must have made a mistake somewhere along the way, but where?

OH right! Silly me! Remember this sentence I wrote before?

In the next block, the program uses the bytes of the code itself in a loop until it finishes to calculate some initial value.

It totally slipped my mind. Using a software breakpoint as we did when executed db 0x40066d is adding CC (INT3) to the instruction and thus it changed the result of the calculation that is done by the loop at 0x400661 . The INT 3 instruction generates a special one-byte opcode ( CC ) that is intended for calling the debug exception handler.

The solution: 2nd try

So, we need to find a way to get the initial value of the key without changing the code itself. That’s where Hardware Breakpoints come in handy. Let’s disable the software breakpoint and put a hardware breakpoint just at the same address:

[0x40066d]> db- 0x40066d

[0x40066d]> doo

Process with PID 1317 started…

File dbg:///home/beet/Desktop/Security/r2con/crackmes/packedup reopened in read-write mode

= attach 1317 1317



[0x7fcb28f77f30]> drx 1 0x0040066d 1 x

[0x7fcb28f77f30]> dc

Welcome to packedup for r2crackmes 🙂

Flag << Blah_Megabeets_Blahaaa



[0x0040066d]> dr ecx

0xdc77df87



Awesome! This time we found the right initial value, let’s put it in the python script and find the flag:

beet:~$ python answer.py [+] Flag: ['\xc8', '*', '\xd9', '>', 'p', '\x0e', '\xef', 'h', '\xf7', '\x91', '\x8e', '\xad', 'c', '\xa7', '\x9b', '\xaa', '\xf0', 'w', '\xd6', '%', 'k', '>', '\xe8', '\x7f', '\xc9', 'o', '\xae', '\xbd', 'k', '\xb1', '\x9d', '\\', '\xd3', 'v', '\xf3', '/', '\\', '<', '\xe8', 'k', '\xf2', '\xaf', '\x8c', '\xa9']

Come on… bummer. We failed again. So, what’s now? What are we missing? I’m sure the initial value is correct now that we used a hardware breakpoint. It is probably something in the algorithm that we missed, but what?

Now that we’re still in debug mode let’s seek again to the start of the checking loop at 0x0040067b , follow each step slowly and compare it with quotations from before where I explained it:

The assembly: 0x0040067b 8b45f0 mov eax, dword [rbp - 0x10]

The explanation:

[✓] Now we are starting the validation loop. The first instruction of this loop moves the value of [local_10h] to eax

The assembly: 0x00400680 d1c8 ror eax, 1

The explanation:

[X] Then, at 0x400680 the value at eax is being left-rotated with 1 byte.

Wait a minute! It was left-rotated ( rol eax, 1 , now it’s right-rotated ( ror eax, 1 )?! Of course we’ll get a wrong flag, our algorithm was all wrong. But how did it changed?

To understand that we should first put a hardware breakpoint on write access to this address ( 0x00400680 ) to see where this address is changed from.

[0x40066d]> doo

Process with PID 1611 started…

File dbg:///home/beet/Desktop/Security/r2con/crackmes/packedup reopened in read-write mode

= attach 1611 1611



[0x7fc43ae24f30]> drx 1 0x00400680 1 w

[0x7fc43ae24f30]> dc

[0x00400600]>

The debug stopped at 0x400600 , let’s print the instruction in this address:

[0x00400600]> pd 1

0x00400600 67 c6 40 6d c8 mov byte [ eax + 0x6d ], 0xc8

That’s it! It modify the instruction on this address. If before the opcodes of rol eax, 1 were d1c0 , now it changes it to d1c8 ! We can use another great feature of radare2 to disassemble hex-string:

[0x00400600]> pad d1c0

rol eax, 1

[0x00400600]> # changed to

[0x00400600]> pad d1c8

ror eax, 1

Let’s print the whole function to understand it more thoroughly.

[0x00400600]> pdf

╭ (fcn) entry2.init 77

│ entry2.init ();

│ 0x004005bd 90 nop

│ 0x004005be 90 nop

│ 0x004005bf 90 nop

│ 0x004005c0 90 nop

│ 0x004005c1 b8 7d 00 00 00 mov eax , 0x7d ; ‘}’ ; 125

│ 0x004005c6 bb ff 0f 00 00 mov ebx , 0xfff

│ 0x004005cb f7 db neg ebx

│ 0x004005cd ff cb dec ebx

│ 0x004005cf 81 e3 14 06 40 00 and ebx , 0x400614

│ 0x004005d5 b9 f6 06 40 00 mov ecx , 0x4006f6

│ 0x004005da 81 e9 14 06 40 00 sub ecx , 0x400614

│ 0x004005e0 ba 07 00 00 00 mov edx , 7

│ 0x004005e5 cd 80 int 0x80

│ 0x004005e7 b8 14 06 40 00 mov eax , 0x400614

│ 0x004005ec 67 c6 40 50 c1 mov byte [ eax + 0x50 ], 0xc1 ; [0xc1:1]=255 ; 193

│ 0x004005f1 67 c6 40 51 c1 mov byte [ eax + 0x51 ], 0xc1 ; [0xc1:1]=255 ; 193

│ 0x004005f6 67 c6 40 52 03 mov byte [ eax + 0x52 ], 3

│ 0x004005fb 67 c6 40 6c d1 mov byte [ eax + 0x6c ], 0xd1 ; [0xd1:1]=255 ; 209

;– rip:

│ 0x00400600 67 c6 40 6d c8 mov byte [ eax + 0x6d ], 0xc8 ; [0xc8:1]=255 ; 200

╰ ╭─< 0x00400605 e9 02 00 00 00 jmp main

We see that this function named by radare2 as entry2.init and it contains a code that manipulates the main function. You can see how 0x7d is moved into eax at 0x4005c1 and then a syscall ( int 80 ) instruction is performed at 0x4005e5 . This syscall is for mprotect as can be found here. mprotect will make the code section writeable. After that, an address at the beginning of main() is moved to eax and some instructions are manipulated by the next instructions using eax and relative offsets. This is exactly where our rol became ror .

.init_array

So what is this entry2.init function and how we missed it? entry2.init , as hinted by its name, is an initialization function. The function is executed prior to the execution of main . I’ll explain it a bit more.

Before transferring control to the application, the runtime linker processes any initialization sections found in the program. The addresses of the functions which are executed by the dynamic linker are contained in the .preinit_array and .init_array sections. The .init_array section contains an array of pointers to functions which will be executed when the program starts. The section contains only function pointers and not code. These functions are also related as constructors. Like the initialization functions, there are also termination functions (destructors) which are contained in the .fini_array section. These functions will be executed when the program exits normally.

radare2 also recognized the .init_array section. Let’s print its contents:

[0x00400600]> f~init_array

0x00600e08 16 section..init_array

0x00600e18 0 section_end..init_array

[0x00400600]> pxW 24 @ section..init_array

0x00600e08 0x00400590 entry1.init

0x00600e0c 0x00000000 section.

0x00600e10 0x004005bd entry2.init

0x00600e14 0x00000000 section.

0x00600e18 0x00400570 entry3.fini

0x00600e1c 0x00000000 section.



f is used to list the flags created by radare.

~ is radare’s internal grep.

pxW is stands for print hexadecimal Words (32bit).

We can see that there are two functions in this array, including our beloved entry2.init .

Just as an extra, you can define an initialization routine in a gcc compiled programs by using a template similar to this one:

void __attribute__ ((constructor)) some_func() { // code goes here }

The solution: 3rd try

So now that we know that entry2.init changed rol to ror we need to modify our script according to this new information. We’ll replace the rol lambda with ror and execute it again.

This is our new script:

# Rotate right lambda ror = lambda val, r_bits, max_bits: \ ((val & (2**max_bits-1)) >> r_bits%max_bits) | \ (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1)) # Byte array from 0x004007A0, modified from the generated results of `pcp 44 @ 0x4007a0` arr = [ 0x0F, 0xC9, 0xA8, 0x86, 0xAC, 0xE0, 0x18, 0x93, 0x8A, 0xAF, 0x91, 0xA2, 0xE4, 0x64, 0x7A, 0x5A, 0x08, 0x8B, 0xA8, 0x9A, 0xB4, 0xD1, 0x1F, 0x84, 0xB4, 0xD1, 0x71, 0x52, 0x1C, 0x8A, 0x80, 0xD2, 0x14, 0x95, 0x82, 0x97, 0x80, 0xD2, 0x1F, 0x90, 0x8F, 0x91, 0x93, 0xA6 ] # Initial key value key = 0xdc77df87 flag = [] # Iterate the array backwards for b in reversed(arr): # ror the key key = ror(key, 1, 32) # xor the 8 lower bits of the key with current byte in array char = chr(b ^ (key & 0xff)) # Add char to final flag flag.insert(0, char) print '[+] Flag: ', ''.join(flag)

And now let’s execute it for the third time:

beet:~$ python answer.py [+] Flag: r2_is_for_packedup_things_like_linux_malware

ICE CREAM! 🙂

Epilogue

Here another article about radare2 is coming to an end. In this writeup I showed you more of the powers within radare2, this time its capabilities to analyze a self-modifying binary. I’m aware that it’s hard, at first, to understand the powers within radare2 or why you should put aside some of your old habits and get used working with radare2. Having radare2 in your toolbox is a very smart step whether you’re a reverse engineer, an exploit writer, a CTF player or just a security enthusiast.

If you want to learn more about radare2 I suggest you to start from the part 1 of my series “A Journey Into Radare2” and from the second part which is more exploitation focused.

As always, please post comments to this post or message me privately if something is wrong, not accurate, needs further explanation or you simply don’t get it. Don’t hesitate to share your thoughts with me.

Subscribe on the left if you want to get the next articles straight in your inbox.