As every year at r2con, the r2wars competition was hosted by sanguinawer and the
r2 overlord pancake. I’ve made it to the second place in this year’s battles, so I’ve promised to create a writeup for my participation – and here it is. Welcome to the nerdiest game in town.
Lol What’s r2wars Again?
The r2wars competition is based on
radare2’s ESIL (Evaluable Strings Intermediate Language) engine. It’s normally used to emulate instructions of various architectures during reverse engineering. It’s definitely a useful feature of
radare2 and you can find more information here if you want to get started using it.
In order to find and fix bugs in the ESIL engine, r2wars was created to have fun and find bugs at the same time. Let’s just quote the description of the r2wars competition for more information:
Participants must submit small programs written in assembly (assembled by rasm2) to be executed with ESIL in a shared memory space inside r2.
The objective of those programs is to find out the other program location and overwrite its code to make it crash, the last program to survive wins.
The idea is similar to Core Wars which is a game that’s already around for many years. In r2wars, there’s a shared memory space of 1KB that’s mapped as
rwx and initialized with null bytes. Both bots get instantiated in this memory space at random locations and the battle begins.
The goal is to corrupt the instruction pointer of the opponent and therefore cause it to crash. Another option is to cause invalid read/writes that also result in crashes. There are many strategies to do this – many of these can be found in the Core Wars article above which is already a good read by itself.
Keep in mind that it’s important to write efficient assembler code, since expensive instructions cause the r2wars scheduler to skip some execution cycles of your bot and I guess you simply don’t want that to happen :)
As soon as the bots are being executed in the r2wars environment, the battle is displayed in a web UI for people to watch:
First, install Mono (yes, really) and compile r2wars with
xbuild r2wars.csproj. After that, place your bots in the
warrriors folder next to
r2wars.exe and start r2wars with Mono.
Some repositories already contain some simple bots you can play around with:
This year the most popular architecture was x86, but there also were some ARM and MIPS bots. I’m going to create an ARM based bot for next year and this year’s winner is based on ARM too, so don’t hesitate to step back from using x86 only. However, for beginners x86 is the best option to begin with in my opinion.
Use this cool one-liner to assemble and debug your bot during development:
radare2 malloc://1024 -c "e asm.arch=x86; e asm.bits=32; aei; aeim; wx $(rasm2 -a x86 -b 32 -f yoloBot.x86-32.asm) @100; aer PC=100; aer SP=SP+100; Vpp"
After the visual view has opened, just step through the execution and fix your bugs.
A part of my strategy was to write as much data as possible in the most efficient way possible. I’ve tried various instructions for this when developing my bot. To profile how well it performs, I’ve used a fixed amount of instructions to execute and examined the memory space afterwards. This is the one-liner I’ve used:
radare2 malloc://1024 -c "e asm.arch=x86; e asm.bits=32; aei; aeim; wx $(rasm2 -a x86 -b 32 -f yoloBot.x86-32.asm) @100; aer PC=100; aer SP=SP+100; 150ds; s 0x0; V\!"
This executes 150 instructions of bot code and opens the visual mode of
radare2. You can now just scroll through the memory view and check for large blocks that consist of only null bytes. Finding such blocks of memory that haven’t been overwritten is an indicator for an inefficient bot, since you might also just miss your opponent in battle:
You can profile variants of your bot using this method and choose the most efficient one afterwards.
I won’t paste the full code here, you’ll have to reverse it yourself :D However, I’m going to describe the general structure:
In the first phase, the bot will populate the registers:
call init init: [...] mov ebx, 0xc3c3c3c3 ; ret instructions mov ecx, ebx ; dito mov edx, 0x400 ; end of arena mov edi, <stage 4 code> mov esi, <stage 4 code> mov ebp, <stage 4 code> call stage_1
0xc3 values are being used to (hopefully) cause the opponent to crash by causing it to perform a
ret instruction. I’m using two registers full of these bytes that will be used in a later stage. To keep track of the end of the arena, which is at
EDX register is used.
My bot is a replicator. This means that it contains code for another execution stage in its registers, namely
EBP. The bot will repeatedly push these registers and perform a jump into the resulting code and execute it all over again. You can find a detailed description of this technique in this post. It’s important to fit the whole last stage into the registers that are still available for use.
In the second stage, the bot wipes the end and the start of the memory region since many bots try to be sneaky and hide in these places. I should’ve also wiped the middle of the memory region since this year’s winner decided to hide there as well. Hmm, maybe next year :)
The third stage migrates into the first quarter of the memory region and initiates the replication process:
mov esp, eax ; relocate to `eax` pushad [...] jmp esp ; execute 4th stage (embedded into registers and now written to memory)
The fourth and last stage performs an infinite loop. It adds a carefully crafted static offset to
ESP (lol), pushes the embedded code and jumps to it again. I’ve tried to fit as many
pushad instructions into this stage as possible. One neat trick allowed me to save additional space for one more
pushad – you can compare two registers and move a value in case one has a greater value than the other in a single instruction:
cmovg esp, eax ; enter arena from start - we are outside
This instruction avoids writing outside of the arena, which would count as an invalid instruction.
And that’s it :)
- You have to search for fast write instructions or otherwise another bot might chase yours and you will lose eventually
- Be aware that people at r2con will and are able to reverse your bot while it’s running on the screen, since the whole code is displayed on there. Looking at you, Anisse :D