all repos — site @ 91e9385e65dcaf50a14a915368696444d31517f9

source for my site, found at icyphox.sh

pages/blog/r2wars.md (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
---
template:
url: r2wars
title: My submissions for r2wars 2020
subtitle: If I learnt one thing, it's that ARM is the future
date: 2020-09-13
---

[r2wars](https://github.com/radareorg/r2wars) is
a [CoreWar](http://corewars.org)-like game thar runs within the radare2
[ESIL](https://radare.gitbooks.io/radare2book/content/disassembling/esil.html)
virtual machine. In short, you have two programs running in a shared
memory space (1kb), with the goal of killing the other and surviving as
long as possible. r2wars was conducted as a part of
[r2con2020](https://rada.re/con/2020).

## day 1

My first submission was an incredibly simple "bomber". All it does is
write code to a location, jump there, and continue executing the same
thing over and over.

```asm
mov eax, 0xfeebfeeb; just some bad jumps
mov ebx, eax
mov ecx, eax
mov edx, eax
mov ebp, eax
mov edi, eax
mov esp, 0x3fc
mov esi, 0x3fd
mov [esi], 0xe6ff60
jmp esi
```

Specifically, it writes `0xe6ff60`, which is
```asm
pushal
jmp esi
```
effectively looping over and over. `pushal` is a very interesting x86
instruction, that pushes all the registers and decrements the stack
pointer `esp` by how many ever bytes were pushed. Nifty, especially if
you're looking for high throughput (to bomb the address space). Here, it
starts bombing from `0x3fc` - `0x000` (and below, because there's no
bounds checking in place), and ends up killing itself, since writing
outside of the arena (`0x000` - `0x400`) is illegal.

Ultimately, this bot placed 7th out of 9 contestants -- an underwhelming
outcome. I had to fix this.

![day 1](https://x.icyphox.sh/gk1i0.png)

## day 2

I sat for a second and recollected the different reasons for my bot
getting killed, and the one that occurred the most was my bot
insta-dying to bad instructions being written from `0x400` -- i.e. from
near where I'm positioned. Nearly all competing bots write from bottom
up, because `pushal` _decrements_ the stack pointer. So the obvious
solution was to reposition my initial payload way above, at `0x000`. And
of course, it goes without saying that this assumes everyone's using
`pushal` (they are).

```asm
mov eax, 0xffffffff
mov ecx, eax
mov edx, eax
mov ebx, eax
mov ebp, eax
mov esi, eax

check:
    mov edi, 0x000
    cmp [edi], 0
    jne planb
    mov esp, 0x400
    inc edi
    mov [edi], 0xe7ff6060; pushal, jmp edi
    jmp edi

planb:
    mov edi, 0x3fb
    mov [edi], 0xe7ff6060
    mov esp, 0x3fa
    jmp edi
```

I also added a (pretty redundant) check to see if the stuff at `edi` was
0, since the entire arena is initially `0x0`. My reasoning, albeit
flawed, was that if it wasn't 0, then it was unsafe to go there. In
hindsight, it would've been _safer_, since it's already been written
over by somebody. In any case, `planb` never got executed because of
what I'd mentioned earlier -- *everyone* writes from `0x400`. Or
anywhere above `0x000`, for that matter. So I'm relatively safer than
I was in day 1.

These changes paid off, though. I placed 4th on day 2, out of 13
contestants! This screenshot was taken on my phone as I was eating
dinner.

![day 2](https://x.icyphox.sh/5ZJfT.png)

All wasn't well though -- I still lost 4 matches, for the reasons below:

1. I'd get snuffed out before my bomb wave from `0x400` would reach
   the opponent.
2. I'd end up bombing myself without hitting anyone on the way up.

## day 3

I needed to add some checks to prevent killing myself in the process of
bombing.
```asm
mov eax, 0xffffffff
mov ecx, eax
mov edx, eax
mov ebx, eax
mov ebp, eax
mov esi, eax

mov edi, 0x000
mov esp, 0x400
mov [edi], 0x20fc8360
mov [edi+4], 0xff600374
mov [edi+8], 0x0400bce7
mov [edi+12], 0xe7ff0000
jmp edi
```

If you noticed, the initial payload I'm writing to the address at `edi`
is a bit more complex this time -- let's break it down.

```
0x20fc8360
0xff600374
0x0400bce7
0xe7ff0000
```

This translates to:
```asm
60                pushal 
83 FC 20          cmp    esp, 0x20
74 03             je     9
60                pushal 
FF E7             jmp    edi
BC 04 00 00 00    mov    esp, 0x400; <- 0x9
FF E7             jmp    edi
```

I check if the stack pointer is `0x20` (decrements from `0x400` due to
`pushal`); if yes, reset to `0x400`, else continue looping. This
prevented me from writing myself over, and also resets bombing from
`0x400` -- better chance of hitting someone we missed in our first wave.

Sounds good? That's what I thought too. Day 3 had a bunch of new bot
submissions (and some updated submissions), and a lot of them checked
`0x000` for existence of a bot, effectively recking me. I placed 8th out
of 14 contestants, with 7 wins and 6 losses. Tough day.

![day 3](https://x.icyphox.sh/IKqxD.png)

## day 4: the finals

I spent a lot of time refactoring my bot. I tried all kinds of things,
even reworked it to be entirely mobile using the `pushal` + `jmp esp`
trick, but I just wasn't satisfied. In the end, I decided to address the
problem in the simplest way possible. You're checking `0x000`? Okay,
I'll reposition my initial payload to `0xd`. 

And this surprisingly tiny change landed me in 4th place out of 15
contestants, which was _way_ better than I'd anticipated! The top spots
were all claimed by ARM, and naturally so -- they had a potential
throughput of 64 bytes per cycle thanks to `stmia`, compared to x86's 32
bytes. Pretty neat!

![day 4](https://x.icyphox.sh/DJbEE.png)

## links and references

- [Anisse's r2wars 2019 post](https://anisse.astier.eu/r2wars-2019.html)
- [Emile's intro to r2wars](https://www.tildeho.me/r2wars/)
- [How not to suck at r2wars](https://bananamafia.dev/post/r2wars-2019/)
- [r2wars: Shall we play a game?](https://ackcent.com/r2wars-shall-we-play-a-game/)
- [Shell Storm's online (dis)assembler](http://shell-storm.org/online/Online-Assembler-and-Disassembler)
- [radare2](https://github.com/radareorg/radare2)
- [r2wars game engine](https://github.com/radareorg/r2wars)
- [Anisse's bot workspace](https://github.com/anisse/r2warsbots)
- [My bot dev workspace](https://github.com/icyphox/r2wars-bots)
- [r2con YouTube](https://www.youtube.com/channel/UCZo6gyBPj6Vgg8u2dfIhY4Q)

## closing thoughts

This was my first ever r2wars, and it was an incredible experience. Who
woulda thunk staring at colored boxes on the screen would be so much
fun?! So much so that my parents walked over to see what all the fuss
was about. Shoutout to [Abel](https://twitter.com/sanguinawer)
and [pancake](https://twitter.com/trufae) for taking the time out to
work on this, and _especially_ Abel for dealing with all the last minute
updates and bot submissions!

All things said, mine was still the best x86 bot -- so that's a win. ;)