Hacking Positional Audio in Mumble

I took a but of a break from working on our Mumble services lately and started working on some open source stuff for Mumble proper. One if the things I got up to was updating a few of the positional audio plugins - I’ve updated the plugins for Counter Strike: Source (which is now in the repo and should work again), World of Warcraft (which still needs some testing, and I’m kind of waiting on a green light from Blizzard over it) and I think I’ve got Battlefield: Bad Company 2 about licked (but again, needs some testing).

So I thought, since it took me a little while to figure out updating plugins, that I’d try write a mini-guide to updating plugins. The HackPositionalAudio page is pretty detailed, but it’s also a little geared towards writing them from scratch and is also quite daunting to a beginner. So here goes:

##How to update an outdated positional audio plugin

The first thing you’ll need is a Mumble build environment, which for most people will basically mean following all the instructions on the Building Mumble for Windows page. Once you’ve got it all set up and you can build a debug version of Mumble successfully, grab the sources for dD0T’s postional audio helper and extract that to your C:\dev folder (so it’s something like C:\dev\pahelper). Then build it, and copy it into your mumble\debug folder:

c:\dev\mumble> cd ..
c:\dev> cd pahelper
c:\dev\pahelper> nmake
c:\dev\pahelper> cp debug\MumblePAHelper.exe ..\mumble\debug\

Note: Mumble doesn’t use c:\dev\mumble path any more for Windows builds, so you’ll have to update your build paths accordingly.

That way the PAHelper can access the plugins you’re building. Note that you can’t rebuild a plugin while PAHelper is running because the .DLL will be in use, so after checking that PAHelper will run from the debug directory (you’ll have to CD into C:\dev\mumble\debug, or double-click MumblePAHelper.exe in Windows Explorer, because it works off the working directory and it won’t find any plugins if you don’t).

Next up, it’s time to start hacking - for this example we will use Bad Company 2. Source engine games like Counter Strike: Source are much easier, because using the console command cl_showpos 1 you can easily see the values that you’re searching for. Games like Bad Company 2 are more difficulty because I don’t know of any way to get the map position, so we brute force it.

Danger! Danger! Danger! We’re going to be using a program called CheatEngine, which is not so-named for no reason. It will set off anti-cheat programs, so be smart and we take absolutely no responsibility if your account gets banned!

Download CheatEngine if you haven’t already, and start the 32-bit version. Start your game up, and hopefully it doesn’t boot you because CE is running - World of Warcraft does, but that brings us to our next point: check the plugin sources and see if there’s any hints on updating it. WoW’s plugin has a forum URL that lists all the offsets for the newest version.

So hopefully at this point if it’s still necessary to brute force search for the new offsets, you’ve got your game running (Windowed mode is easiest) and CheatEngine running as well. Click the glowing computer/magnifying glass button on the toolbar that says “Select process to Open”, and then search for your game’s process. It’s probably something like 00001234-BadCompany2.exe - double-click it to open.

Now we need to find a server to hunt on - it’s easiest to do this on an empty server, so you don’t have to worry about being shot and killed. The most important thing however is that you find a server that does not have PunkBuster or VAC or anything of that nature on it. So in the case of BC2 you’re looking for an empty, un-ranked server with no PunkBuster.

Once you’re in, and spawned on the ground - now’s when we begin hunting. Look for a house with multiple floors - the ones with the attics are easiest I think, but even a ladder or a hill will do. I think it’s easiest to look for the Y values of your position first, hence the need for some vertical movement. On the right hand side of the CheatEngine window is the memory scanner. What we’re searching for is a float (almost always - but the plugin source will detail if it’s another data type), so pick float from Value Type. We don’t know what the initial value of our height is, so pick Unknown initial value in Scan Type. Now click First Scan.

This will scan the entire of the process address space - depending on your PC it may wreck it for a bit, making it run really slowly. Wait for it to get done, and don’t move in-game while it’s scanning. It shouldn’t take more than a few seconds. Now move up the stairs/ladder/hill in game, then change Scan Type to Value Increased and click Next Scan.

Again, wait for it to do it’s thing before moving again. Repeat the process until you’re as high as you can get and you’ve scanned it again. Depending on the game and how lucky you are, you might be looking at between 100,000 and a million addresses right now, which is a pain so let’s whittle them down more. Run down the stairs a little bit, then change Scan Type to Value Decreased and click Next Scan. Repeat that process a few times until you’re down to 1,500 addresses or so.

If you go up and down the stairs a bit and your address list is looking a bit long, find another set of stairs and repeat increased/decreased process there a bit.

Now on the left side, where the found addresses show up, scroll down the list until you find some addresses that are green. You’re looking for something that increases when you jump, so jump in game a few times and watch the addresses. When you see one that looks promising, double-click it. This will put it in your “cheat table” down the bottom. Double click it a couple of times to have a few copies of it, and now go consult the sources to the plugin.

What we’re looking for are typically three floats, four bytes each, then 16 bytes up or down from that (depends upon the game) should hopefully be the heading information. On Bad Company 2, it’s 16 bytes down from your position for the front heading, and 16 bytes down more for the top - and there’s no fancy math necessary to turn the heading into a spherical coordinate like we need. So for BC2, if we think our position Y is at 0x01571E94, then subtract 0x10 (which is 16 bytes) to get the front Y and another 16 to get the top Y. So double-click one of the entries in the cheat table and change 0x01571E94 to 0x01571E84, then double-click another and change it to 0x01571E74. You have to double-click the address to change the address, double-clicking other fields changes them instead. Also note that when you open up the address editor, it may not match what’s in the address box - simply subtract 0x10 from whatever’s in there and check it after you click OK.

Now watch the cheat table while in-game, and look up and down. One should go up, and one should go down, as you look up and down in game - for BC2 they should both be between -1.0 and 1.0 at all times. If they’re not, or they don’t change, clear your cheat table and move on to the next possible address candidate.

If they behave as expected, write those addresses down, and open up the plugin in your favorite editor. On Bad Company 2, the vertical position is Y, which is the second of three floats. If our Y position is at 0x01571E94, the actual address of the X/Y/Z triplet is 0x01571E90, so subtract 4 from each of the addresses and then put them into your plugin in the correct locations:

 is_steam = (strncmp("Score:", sMagic, 6) == 0);

 if (is_steam) {
  ok = peekProc((BYTE *) 0x01571E90, avatar_pos, 12) &&
       peekProc((BYTE *) 0x01571E80, avatar_front, 12) &&
       peekProc((BYTE *) 0x01571E70, avatar_top, 12);

Uh oh, what’s this “is_steam” business? Well that’s something else we have to search for: games that have multiple builds will have different offsets for each build, so the plugin needs to be able to tell them apart. Scroll up and you’ll see:

 if (!peekProc((BYTE *) 0x015715b4, sMagic, 6)) {

Fortunately for BC2, this is easy enough to search for. The string “Score: “ is in a specific spot on the Steam version, and on the Retail version it’s somewhere else. Go back to the Memory scanner and click “New Scan” to reset everything. Now in Scan Type pick Array of Bytes, make sure the Hex box is checked, and in the Array of Bytes text box paste 53 63 6f 72 65 3a. That’s a hex representation of “Score:” in ASCII, which should be pretty easy to track down. Click “First Scan” and not many results should show up. Look for the first green one, and put that address in sMagic as above.

Now cross your fingers and save the plugin file if you’re working on BC2. On other games, the Camera may not be the same coordinates as the player, so you’ll need to find those offsets too - check the plugin sources, because there may be other offsets you need to track down. If you’re done, and you’ve saved the plugin, go back to your Mumble build environment and build a debug version of the plugin:

c:\dev\mumble> cd plugins\bfbc2
c:\dev\mumble\plugins\bfbc2> nmake

Now if it built successfully (watch out for syntax errors in your pasting, or if you tried to build with MumblePAHelper running), you should be able to double-click MumblePAHelper.exe and it should pop up. Ideally it should link to the game quite quickly to the game and you should see some meaningful numbers in the coordinates box. Move around in game while watching the numbers: All three “front” numbers should range from -1 to 1. If you turn your character around until X is exactly -1, then walk forward, the X result for “position” should decrease. If it does, success! If not, repeat the process and try again. :(

If it all looks sane, now’s when we test if these addresses are dynamic or not. Reboot your whole computer, then start the game and change every setting you can find - resolution, detail, etc. Start a game and PAHelper, and see if the values still look correct. If they do, start your Mumble build environment up, and build a release version of the plugin:

c:\dev\mumble> cd plugins\bfbc2
c:\dev\mumble\plugins\bfbc2> nmake release

Now look in C:\dev\mumble\release\plugins for bfbc2.dll, zip it up and send it to some friends and test if it actually works.

##Adding a self-built plugin to Mumble:

  1. Start Mumble, and pick “Settings” from the “Configure” menu.
  2. Make sure “Advanced” is checked on the bottom left side.
  3. In the “Audio Output” tab, make sure “Positional Audio” is checked.
  4. In the network tab, uncheck “Download plugin and overlay updates on startup.”
  5. In Windows Explorer, open %APPDATA%\Mumble\plugins, and paste your new plugin (must be a release build, debug probably won’t work) there.
  6. Back in Mumble’s configuration screen, in the “Plugins” tab, click “Reload Plugins.”
  7. Make sure “Link to Game and Transmit Position” is checked.

Everyone has to follow these steps - if someone doesn’t deactivate plugin updates for example, Mumble will dutifully clobber the self-built one with whatever’s in the public repo at the time. Now you should be able to test if the plugin is doing it’s job.

If everything checks out, go back and update things like the build number in the plugin description, and then submit the patch to the Mumble developers somehow (usually by creating a pull request on Github).