Between September 6 and 9, the second radare2 conference (r2con) took place in Barcelona. This security conference is mainly focused in radare2, an open-source framework for reverse engineering with a great and active community. Ackcent was there, during the trainings (Wednesday and Thursday) and talks (Friday and Saturday), but also solving some crackmes and playing the r2wars competition. In this blog post, we will share what was the motivation to play, how we programmed our warrior, a brief summary of the r2wars competition day, some conclusions and final thoughts.
The r2wars is a game similar to corewar, where:
The allowed assembly languages for this first competition were MIPS, ARM, x86, but in a future, could be others that currently are also supported by ESIL engine. This engine emulates the execution of combats, translating known assembly languages into an intermediate language (ESIL). If you are interested in CPU emulation, check ESIL and unicorn.
In the r2wars competition each warrior fights with the others. The warrior that wins more combats, wins the competition. To win a combat, a warrior has to win 2 of 3 rounds. A round or a combat, can be won, lost or tied. If two warriors are tied, because they have the same combat results, the overall index (total numbers of rounds won) is used to break the tie. If the overall index is also the same and the warriors are in podium positions, there is a last round between them to get a winner.
If you enjoy writing assembly code (e.g. to overwrite return addresses), analyse it (e.g. to understand a new malware sample), or you think it is something you want to learn because it seems interesting, this competition is for you because you will have to:
In our case, it was a combination of all of the above and the challenge to play a new low-level competition, with also the cool idea of having fun watching two viruses (sorry, warriors 🙂 fighting in memory to survive.
Yes, the name of our warrior is replicator and is written in 32 bits x86 assembly language.
The warrior name has a direct relationship with its combat strategy, but first, let us explain the main strategies used in corewar warriors:
In the end, it seems there is not a perfect strategy, because it also depends on:
With the above in mind, we programmed a warrior with three main characteristics:
Then, the resulting assembly code for our warrior is the following:
<pre>; first="" stage="=" all="" instructions="" below="" mov="" al,="" 0x50 ="" ;="" position="" cx,="" 0x400 ="" limit="" of="" battle="" arena="" edx,="" 0xdddddddd ="" illegal="" opcodes="" ebx,="" edx ="" more="" second="" next="" 3="" edi,="" 0xcc39c401="" esi,="" 0xc489037c="" ebp,="" 0xe4ff6044="" esp,="" eax ="" stack="" pivot="" to="" pushad ="" copy="" jmp="" esp ="" execute="" stage<="" pre="">
Let us explain each line:
mov al, 0x50 ; First position
We load the value 0x50 into EAX. This value will be the size of our jumps and the first memory address where our second stage will be replicated or backward copied (normal pushad instruction behaviour).
mov cx, 0x400 ; Limit of battle arena
We load the value 0x400 (or 1024) into ECX. This value will be used to know if we can replicate the second stage or we are outside the battle arena and we have to jump to the EAX memory address.
mov edx, 0xdddddddd ; Illegal opcodes mov ebx, edx ; More illegal opcodes
We load 0xdddddddd into EDX and EBX. These registers together with ECX and EAX, create a tail of illegal opcodes (mainly the first 8 bytes), that can overwrite the opponent warrior code to kill him.
mov edi, 0xcc39c401 mov esi, 0xc489037c mov ebp, 0xe4ff6044
The opcodes of the second stage. Let us disassemble them:
<pre>; mov="" edi,="" 0xcc39c401="" add="" esp,="" eax="" cmp="" ecx="" ;="" esi,="" 0xc489037c="" jl="" 5="" ebp,="" 0xe4ff6044="" inc="" esp="" pushad="" jmp="" eax<="" pre="">
We jump ESP forward 0x50 bytes.
cmp esp, ecx
After that, we check if we are outside the battle arena.
mov esp, eax
If we are outside, we jump to the first memory address (EAX) and increment ESP. You are right, last instruction is totally unnecessary. It was inherited from previous versions of our warrior.
pushad jmp esp
If we are inside, then we replicate the second stage and jump to it (EIP = ESP).
With that, we finished the explanation of the second stage and we come back to the three last instructions of the first stage.
mov esp, eax ; Stack pivot to first position pushad ; Copy second stage jmp esp ; Execute second stage
These instructions have the same logic than the last ones of the second stage but with an unconditional jump to our first memory address (trying to become a hunter).
In the following video, we can watch a combat between demo warrior (initially loaded at address 0x86, as hunter) and our warrior (initially loaded at address 0x163, as prey). After some instructions executed, our warrior replicates itself to address 0x30 and becomes a hunter. After some more instructions, our warrior overwrites the opponent warrior and wins the combat.
The competition was on Saturday noon. Nobody participated in the MIPS or ARM competition. For the x86 competition, there were 8 warriors and in less than 30 minutes each one had fought with the others. The final classification was:
To break the tie between the second and third position there was a last round that we were lucky to win. It is curious to note that the first three warriors chose a replication strategy. We finished second but we received these great prizes:
It has been a nice experience and a great way to learn by playing. For this reason, we thank @pancake and @skuater for making possible the r2wars competition.
We expect the same competition for the next year but with more warriors participating, other architectures (e.g. x64) and also an improved visualization.
In our opinion, the following ideas could improve next year competition:
Last but not least, we want to end the blog post with a question to think about. It seems that, at a first glance, there is not a perfect warrior strategy but could there be a better approach than writing a handmade warrior? We have one in mind, do you?
See you next year on the battle arena!