Devil Swapper challenge


This is a write-up for solving the devil-swapper RE challenge. It was mostly intended for my personal archive but since it may be interesting to all of you I will try to format it in a clear and most importantly beginner friendly way for people like myself…

important note: The introduced tools may vary for each investigated binary. The following were just my choice for the given scenario!

Anyway let’s get right into it.

The binary

Binary download
Binary BinaryNinja file download


Building the binary obviously.

Copied from the challenge which is linked above for people who want to follow what I did:


cat textfile | base64 -d | gunzip > challenge && chmod +x challenge



First examination

Running the file


Crackme for 0x00sec
Greetings from pico!

Keep trying...

Welp what did I expect..? Running the file will only reveal that it’s not working as intended :) …

Checking the file

$ file binary

$ binary: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=840893e41881866970e0e1a3bbf5673590fe803d, stripped

So I have a stripped 64-bit ELF binary to work on.

The objective is it to find a secret key and the secret message. Whatever that means to us up to this point. Next I want to know if there are any strings within the binary which can help me.

Checking for strings

$ strings binary

Crackme for 0x00sec  
Greetings from pico!  
Keep trying...  
Purpx qq pbai bcgvbaf!  
ntu1~14.04.3) 4.8.4  

There is one particular string in there which could hide some valuable information or hints! I’m talking about this boy here:

Purpx qq pbai bcgvbaf!

No question some kind of cipher.. Since 0x00pf said it would be a simple challenge I assumed some form of caesars ciphers. After trying for a while I found out it was indeed one with a keylength of k=13. Exactly this kind of cipher is known under a different name as well : ROT13

What is the decrypted text?

The answer is:

Check dd conv options!

So here is the dd man page. Let’s look at the possible conv flags:

Each CONV symbol may be:

  • ascii - from EBCDIC to ASCII
  • ebcdic - from ASCII to EBCDIC
  • ibm - from ASCII to alternate EBCDIC
  • block - pad newline-terminated records with spaces to cbs-size
  • unblock - replace trailing spaces in cbs-size records with newline
  • lcase - change upper case to lower case
  • ucase - change lower case to upper case
  • sparse - try to seek rather than write the output for NUL input blocks
  • swab - swap every pair of input bytes
  • sync - pad every input block with NULs to ibs-size; when used with
  • excl - fail if the output file already exists
  • nocreat - do not create the output file
  • notrunc - do not truncate the output file
  • noerror - continue after read errors
  • fdatasync - physically write output file data before finishing
  • fsync - likewise, but also write metadata

Now one could associate the name of the challenge with one of the flags :p ..?

But just swapping every pair of input bytes with

$dd if=binary of=binary2 conv=swab

would not make much sense would it?

Closer look at the binary

Checking the binary within an analysis framework

Anyway we can examine our binary a bit more in detail here. In this challenge I used binary ninja. Any other tool of your choice will work as well of course.


We can clearly see that we have a bunch of subroutines and start out with pushing and moving some values which results in the friendly greeting in the main function. It’s getting interesting after that tho! When the cmp command get’s executed we compare some content at the address 0x40051d with 0x2ba5441.

In short: we compare some opcodes to a value!

Afterwards we have a jump condition. If these compared values are not equal we jump to the branch, which displays “Keep trying”. This results in a terminated program as we saw when running it earlier. So the goal probably is to make it take the other route!


By swapping only the bytes at the offset 0x40051d to not destroy the binary.

How much swapping has to be done?

There is this little command called:

$readelf -flags binary

This command can show a huge amount of information about an ELF binary. Manpage here

From here we can investigate the section headers more closely.

$readelf -S binary


If we look closely we can find the exact adress of 0x40051d again:

  • .data segment
  • adress: 0x30051d
  • offset: 0x51d <=> 1309 bytes
  • size: 0xa9 bytes <=> 169 bytes
  • flags: AX (alloc, execute)

This looks weird at second glance. Why? Because a data segment should hold data, not executable files! So we can conclude this might be the obfuscated part of the binary.

Building the new binary

We want to use dd with the conv=swab flag. We found out the address, the offset and the size of the segment. We can easily build our command now:

$dd if=binary of=binary bs=1 count=169 skip=1309 seek=1309 conv=swab,notrunc && chmod +x binary

This swaps exactly these bytes in our binary!

important note: Do not attempt to create a new binary file out of this through specifying of=new_binary!

Let’s take a look at our new binary in binary ninja


As we can see the graph changed. We avoided the “Keep Trying!” message for now. Let’s see what we get when running the file.

Running the new binary

Crackme for 0x00sec
Greetings from pico!

Wonderful we are getting greeted by the usual but this time we get a little extra too! A brand new shell prompt!

Finding the flag

Crackme for 0x00sec
Greetings from pico!

$ abcdefgh
bash: bcdefgh: command not found


  • A first check with a random input shows us that something from our input returns some content!
  • Only the first character of the input seems to be accepted.
  • After getting a (broken) return value our binary terminates and our normal shell tries to execute the rest of the input

The flag

So to find the flag one could brute force their way through now. Only one character input gets validated. So around 70 possibilities should exist one can enter as input. We can easily bruteforce our way in..

As an alternative one can look at our graph in binary ninja and see where the input gets read & validated through some static analysis, or we tackle this problem with some dynamic analysis through gdb. Either way sometimes we just have to use the force even if it’s brute force :p


$ ./binary
Crackme for 0x00sec
Greetings from pico!

$ 1
Well Done!!


This “small and not so sophisticated” challenge was really fun diving into. I enjoyed it a lot, even as a rookie in that area. For people wanting to try out all of this I have a few closing words. Just do it! I’ve struggled a lot too, but in the end I learned so much from this challenge. Knowledge which I can use for my next binary I want to investigate or reverse engineer! So just bring some time and don’t give up easily :) . Doing this is like puzzling for grown ups with a much higher frustration but also rewarding factor!