 Uh, thank you all for coming. I know you're all missing church to be here so I really appreciate it. Uh, so my name is Jack Baker. Uh, I'm just gonna get right into it, uh, by talking about WebAssembly. So I wanna start, uh, by talking about what WebAssembly actually is. Uh, so you may already be familiar with this, bear with me, but, um, I has a little bit of background. Uh, developers have done and continue to, uh, really incredible job of speeding up JavaScript in your browser with stuff like JIT compilers and a million other optimizations. Uh, the problem is that JavaScript is such a dynamic language and that dynamic nature, uh, will always be a roadblock for optimizations. There's only so much a JIT compiler can get away with. Uh, so what WebAssembly does is it provides sort of a compliment to JavaScript in the form of a static, pre-compiled binary format that can be used for, like, performance intensive applications and it's much more, uh, much easier for your browser to optimize. Uh, so from a practical sense what WebAssembly actually is, is it's basically an instruction set like a simplified CPU architecture that runs in your browser. Uh, so WebAssembly is built to be targetable by existing compilers and existing languages, meaning we're finally at the point where we can start writing web applications and see. Awesome. Uh, so, uh, I want to talk a little bit about what WebAssembly is actually used for. Uh, so as you probably gathered from the name of the talk, uh, WebAssembly is used for video games and this is actually becoming really common. If you look at any of those, like, old school, like flash game sites, you know, new grounds, congregate, stuff like that, what you'll notice is that a huge portion of the new stuff that's uploaded is actually using WebAssembly as the back end. And that's with good reason, uh, because the two really common engines that you'll run into these days for video games are Unity 3D and Unreal Engine 4. And both of these engines can now target WebAssembly. So you can build your Unity 3D game, you can click a couple buttons and it's running in your browser using WebAssembly as the back end. So as a hacker, this means that there's a ton of targets out there. There's not really a lot of tools to interface with them. Uh, just for completion sake, I should say that WebAssembly is used for a lot of different stuff, not just video games. So like, you'll see a lot of like desktop applications that have been retargeted as, uh, web applications. Anything 3D is a really good candidate for WebAssembly. Obviously you'll see crypto miners cause we live in a horrible world. Uh, and there's, there's all sorts of other stuff. Uh, so the reason I say this is that these techniques are not, the techniques I'm gonna show are not really WebAssembly, er, I'm sorry, not really video game specific. Video games are just really the most common and in a lot of ways the most fun targets you'll run into. Uh, so with that in mind, I want to talk about reverse engineering WebAssembly. So the interesting thing about WebAssembly is that, uh, it makes Web reverse engineering start to feel more like traditional, like binary reverse engineering of like L files, P files. For instance, we're back to using a disassembler. Uh, so there are a few tools, uh, that actually support WebAssembly as is, but it's mostly for static analysis. So there's stuff like Radare too, there's like no chance I'm pronouncing that right. Uh, JebD compiler has some experimental support for WebAssembly. And then there's some WebAssembly specific tools like the WebAssembly binary toolkit or Wabbit. Uh, but that's mostly static analysis. When we talk about dynamic analysis, the story changes a little bit. If you're talking about debugging WebAssembly, you're probably talking about using your browser. And if you've used the debugging tools in your browser for WebAssembly, you've probably noticed that they're pretty lacking. So you'll see that there's no like watch points, uh, there's no conditional break points and there's a ton of random bugs. Uh, so with that in mind, I want to talk a little bit specifically about what video game reverse engineering is. So video games are actually a really unique challenge when it comes to reverse engineering. And this is for a couple of reasons. The first is that video games compared to like other types of applications are so much more complex. Because when you're looking at a video game, you're not looking at just the code that the game developer wrote. You're looking at the entire game engine, you know, the graphics, the sound, physics, everything like that. It makes for so much code to look through, uh, that it, it can be daunting. Um, the second thing is that video games are of course more performance intensive than most other applications. And performance impacts are a lot more noticeable. If you're using some, you know, dynamic analysis tool and it's slowing down the game, no one wants to play a game at 5 FPS. It just doesn't work. So there's an extra challenge when you're developing tools for dynamic analysis or hacking video games. So with that in mind, when I started looking at hacking WebAssembly games, what I was really looking for was a tool like cheat engine specifically for WebAssembly. So I want to talk about what cheat engine is. Um, if you haven't heard of cheat engine, uh you probably didn't play MapleStory growing up. But what cheat engine is, uh, is it's a tool made by a guy named DarkBite and it's effectively a specialized debugger specifically for hacking video games. So what cheat engine can do is it can search memory, it can modify memory, it can freeze memory, meaning that attempted changes to that memory location won't take. Uh, it can use watch points and it can inject or patch code in the binary. So that sort of makes sense, but to understand why cheat engine is so cool, why it's worth trying to, you know, emulate these methods, I want to go through sort of a 101 example of using cheat engine. So with that, uh, imagine we're, we're playing our game and our goal is to make our player character invincible. And the one thing we do know, uh, we haven't done any reverse engineering, but the one thing we do know is that our player currently has five health. So what we do is we ask cheat engine, look for everything in memory that has the value five. And cheat engine comes back and it says, okay, I found, you know, a few thousand places where I found the value five. Uh, that doesn't quite narrow it down. Uh, it's way, it's still too much to look through by hand. But uh, it starts to narrow it down. And so what we can do then is, uh, we cause our value to change. We get hurt. Uh, now our health value is four. And we search for that and we say, okay, of all those values that used to be five, which ones are now four. And we narrow it down some more. Um, and then we continue that process as many times as we need to until we've found the one value in memory that is associated with our health value. And once we've found that, that memory address, we can manipulate it. We can heal ourselves back to full. We can give ourselves more health than we're supposed to have. Uh, we can even freeze our health so that basically any attempt to change it, you know, decrement our health when we get hurt doesn't take, it just goes back to five. Um, so just, just like this, with just a couple pretty quick steps, we were able to go from knowing nothing about this, uh, application, no reverse engineering, to doing something practical. We were able to find some value and manipulate it. The problem with this method is that it can take a little while, um, and it needs to be re, needs to be redone every time we play the game. Odds are the next time we start up the game, our health value is going to be somewhere else. Either because of like address based layout randomization or just the fact that uh, dynamic memory, uh, mapping is not really predictable. Uh, basically, we probably have to start from scratch every time. So ideally what we want to do is just do this once and then make some permanent change so that we don't have to go through this process every time we want to do this to the game. So, uh, the next step we take is we set a watch point on our health address and then we get hurt again to trigger it. And then cheat engine comes back and it says, uh, uh, something tried to write to this value in memory and it occurred right here. And so basically, uh, we're able to see exactly which code is responsible for causing our player character to get hurt. Uh, and what we can do once we find that is we're able to patch it out. Basically take out the ability for our character to get hurt. And so with that we've made a permanent change to the binary so that our character is now invincible. Um, so the reason that I go through this example is that it shows that not only is cheat engine, you know, helpful for like hacking video games, we made our character invincible, but it's also a huge help when we're talking about the reverse engineering process. Like we said, um, video games are like dauntingly large as far as applications go when you're talking about reverse engineering. So going, going through this process, we were able to narrow down this gigantic code base and, uh, associate a value in memory with its purpose and then associate that value in memory with the code that affects it. We were able to really easily find the relevant code without doing a whole ton of work. So this is what's so cool about cheat engine. So, um, with that in mind, I had the question of, well, could I apply these same techniques to web assembly? And you run into one really big road block when you try to do that. And that's that web assembly doesn't have watch points. And that was a big, uh, a big part of, uh, the cheat engine process was using watch points. So my question was, can we emulate the behavior of watch points without them being, uh, without them being supported by the spec or by our browser or anything like that? So, uh, my first attempt at doing that was basically the dumbest, simplest thing I could come up with, which was just use your built in browser debugger, put a break point at every single load or store instruction in web assembly. And then when that break point is hit, uh, check, do some math to check if that access is going to affect the address we're watching. And this amounts to thousands upon thousands of break points in, you know, any decently sized video game binary. Um, so as you can probably imagine, didn't work at all. Um, browser basically becomes completely unusable, you know, X kill chrome, start again, no chance. Uh, so I started thinking if I wanted to do this in a way that wasn't like stupid, uh, what would that look like? Uh, so from like a high level perspective, what I came up with was if we want to emulate the behavior of watch points, what we want to do is inject code into the binary at every single memory load or memory store instruction. And what that code is going to do is it's going to check if that access is affecting a memory area that we're watching, our, our watch point address. And if it does, we want it to call a break point, call our code to basically alert us that this change was made, maybe give us a stack trace, something that tells us something tried to change our memory address and it occurred right here. So to do all this, we need to employ some form of binary instrumentation. So let's, uh, talk about that term a little bit. Uh, so binary instrumentation, uh, basically is the process of manipulating an application binary, whether that's, you know, an ELF, a PE file, whatever, uh, to aid in some form of analysis. So there's a lot of really cool binary instrumentation tools that already exist for other types of binaries. So like Frida is an awesome tool for, you know, ELFs, PEs, Android applications, stuff like that. Dynamo Rio is another real cool, uh, real cool tool. Um, there's, I'm sure there's others I haven't used, uh, but this is not like a new concept. I'm not coming up with the concept of binary instrumentation. Uh, there's even some existing tools specifically for WebAssembly. So I want to talk about those a little bit. The first one I want to talk about is a tool called Wasabi by, uh, Daniel Layman, I hope I'm pronouncing that right. Uh, and it's a really cool instrumentation and analysis tool specifically for WebAssembly. Uh, unfortunately it doesn't really fit our needs for a couple of reasons. The first is that Wasabi is written in Rust and it's intended to be run from the terminal. So if we want to instrument some WebAssembly binary, what we have to do is we have to download it. We instrument it from the terminal. We upload it again and then we run it. Uh, and that's, that's a little annoying. Potentially we could modify Wasabi and compile something that could run in the browser. But then we run into our second problem. And that's that Wasabi does all of its analysis by injecting JavaScript into the binary. So basically, Wasabi will put calls into the WebAssembly binary to JavaScript functions in order to do its analysis. And in testing this, it became really clear really quickly that if we want to run a game at any decent performance at all, uh, we need to call JavaScript as infrequently as like, as we can. Uh, so basically if you imagine the WebAssembly VM and the JavaScript VM are two different virtual machines and calls between the two are still a little bit, uh, performance intensive. Uh, and if you're talking about injecting code at every single memory load or memory store instruction, excuse me, uh, this just becomes completely overwhelming for the browser very quickly. So unfortunately, Wasabi didn't work for us. Let's look at our next tool, which I've actually already mentioned, which is the WebAssembly binary toolkit or Wabbit. Uh, so Wabbit is a library specifically for, uh, modifying and parsing WebAssembly binaries. Um, there's even a version of it that's been compiled to JavaScript. So it seemed like the perfect starting point, uh, for, for doing this type of manipulation. The problem is that, uh, in, in reality when you try to use Wabbit to parse like, uh, a video game binary, it just takes way too long and way too much memory. So I tried this with like a demo Unity 3 binary, nothing too big, basically just hello world of Unity. And what you would see is Wabbit would spin its wheels for 10 or 15 seconds and then just crash with an out of memory error. So already we're seeing it's taking way too long, 10 or 15 seconds is way longer than we want to wait, even if it worked, but it didn't. It's running out of memory. Who knows how long, uh, uh, in the, uh, parsing process. So after looking at, uh, the two tools I could find, I sort of drafted a set of prerequisites for what we would want in a tool that we could use for our purpose. So the first thing is we want to be able to instrument binaries from within the browser. We don't want to have to download something, instrument it, put it back up. That's, that's boring. Uh, it also needs to be able to handle large WebAssembly binaries, you know, video game binaries are very commonly like 40 MB plus. Uh, it needs to be able to do this quickly and without running out of memory in the process. So since, as far as I know this tool didn't exist, uh, I developed my own library which I very creatively titled the, uh, WebAssembly Instrumentation Library or Wail. Uh, and this is, uh, Wail is a JavaScript library, uh, specifically focused on making targeted modifications to WebAssembly binaries. Uh, so if you think of a WebAssembly binary, kind of like an L file, uh, uh, an L file has sections that have different purposes and so does a WebAssembly binary. So what Wail does is it can add entries to any, uh, any section of the binary. You can edit existing entries of sections. You can also add or remove sections from the binary. Uh, so Wail actually uses a couple of tricks to modify binaries quite a bit faster and with a lot less memory usage than other libraries. The first one's kind of a no-brainer. Uh, Wail only parses the sections or elements of sections that are necessary to perform the modifications you defined. If you're doing some modification that doesn't require parsing the code section, it's not going to parse it. It just skips it and that saves a ton of time right off the bat. Uh, the second part is a little bit more complicated. So what Wail does is it parses binaries basically as a stream. And I want to explain a little bit about what that means. So if you're talking about parsing some sort of binary, whether it's an application or an image or whatever, uh, the normal way you do that involves, you know, you break that binary up into its pieces, you create a map of all those pieces, and then once you've got that map created, you change the parts you want to change, and then you stick everything back together into one big binary. This is convenient, it's straightforward, it's sort of the obvious way to parse a binary, but it's also sort of slow and a little bit more memory intensive. And the reason for that is that you have to, uh, split the binary up, store every single piece as a different object in memory with all the memory overhead that might entail, uh, and then do the work of putting it all back together. It takes a little while. So what whale does is, as I mentioned, it parses binaries as a stream, meaning it, uh, handles and modifies every single element as soon as it's read. Basically it goes from, goes through the binary from the start, reading only one element at a time, changes it if it has, changes it if it has to, throws it away if it has to, and then it moves onto the next one, forgets that last element ever existed. This way we don't ever have to do the work of storing the entire binary and all its pieces in memory at once. We're just working with one single element at a time and then forgetting about it once we move onto the next one. Uh, there are some downsides to this approach. Uh, the first one is that the parser can never go backwards. Once we finished parsing past a particular element, we've stopped looking at it. We can't go back and we can't make changes to it. We have to do everything, uh, everything we need to do to that element the second we see it. Uh, so to deal with this, we have to define all of our modifications before we start parsing. There's no moment where we can parse the entire binary and then say, okay, I want to change this or that or whatever, we have to define everything up front and then let the parser run with it. This sort of causes a second problem, which is sort of WebAssembly specific. So, uh, when you're talking about modifying a WebAssembly binary or making additions, uh, there's a lot of cases where making one addition or making one change will rely on information about another section or another, another element. So I want to give the example of adding a new function into a binary. So you're adding a new, like, built-in function that can be called from the WebAssembly VM. This is actually a three-part process. The first thing you gotta do is you, uh, define a new element in the type section, which says this is a function, it takes two parameters, one is an int 32, the other one's a float 32. It, it returns one value and that value's also a float 32. Something like that. Uh, the next thing you have to do is you add an element into the function section. And this basically declares your function without defining its body, without defining the instructions that are actually, that actually make up the function. And when you create this function section entry, you have to reference the index of your new type section entry. So if that new entry you've created in the type section is index 1,000, your function section entry has to say, I'm a function, I reference type element 1,000. Um, and then you have to create an element in the code section that is basically the body of your function. Basically, uh, the instructions that will be called when your function is called. And this section also has to correspond to the index of your app, your newly created function element. Uh, similar to with the type section. Uh, so as you can sort of see with this, there's a couple different pieces of information that we don't know up front. We don't know what index our new type section entry is going to be until we know how many existing type section entries this is going to be. So this causes a problem when we're talking about doing everything up front because there's some knowledge that we don't have up front. So, uh, Wail does with the, Wail deals with this using sort of a special grammar. Basically every addition you make to a binary, you're given back a handle. And this handle doesn't have a value until the parser starts running, but it can be used to make these complicated additions. So in the previous example, we add our new type entry, we're given back some handle, and then we use that handle when we define our new function entry. Uh, and then we use the handle from that new function entry to create our code entry. And basically by doing this, the parser will see these handles fill in the gaps for us. And so we can do these complex like chained modifications while still doing everything up front. We still don't have to know anything about the binary before we start parsing it. Uh, if you're not already sick of, uh, hearing me talk about WebAssembly, uh, parsing, there is one more really annoying, uh, catch. And this is actually a two-part problem. And it has to do with the, uh, the function tables and the global variable tables. So, uh, if we take the example of like calling a function in WebAssembly, you're not calling that function by its address or by its name, you're calling it by its index into a table. And so, uh, that table is created by the engine when it first starts parsing the binary. So basically what the, what the engine does is it takes all the imported functions, all the functions that have been sort of given to the WebAssembly binary by JavaScript, and it puts them at, as the first elements in the table. So if you have ten imported functions, the first one will be zero, one, two, three whatever, all the way to nine. Uh, and then it takes all the internal functions, the functions that are actually built into the binary, uh, and it puts them in the table after those imported functions. So the first internal function will, if you have ten imported functions, the first internal function will be index ten, eleven, twelve, whatever. And the exact same process exists when you're talking about global variables, whether they're imported or internal. So the problem here is that the second that you add a new imported function or imported global variable, you throw off the entire rest of the table. So if you have, you know, your ten imported functions and you add one, what used to be in, uh, function index number ten, your internal function is now eleven. Eleven is twelve, etc. And that throws off every single call, every function, uh, reference, pretty much the rest of the binary is broken. Uh, and the same with global variables. So this is really annoying, but it's actually pretty easy to fix automatically. So Wail fixes this basically by parsing, uh, any section that could potentially be affected by this change. Uh, and it finds those elements and it says, oh, uh, you are, you're now off by one, I add one to that, uh, in order to fix those references so that you don't have to do the work yourself. So now that we've talked about the boring stuff, uh, and we've got our, our parsing tool, we can actually go back to our goal of emulating the behavior of watch points, uh, using Wail. So let's talk specifically about what that process looks like. Uh, so the first thing we're gonna do is we're gonna create two new global variables within the binary. Uh, and so the first one of those is gonna hold the address that we're watching, literally the address of our watch point. The second one is gonna hold two different flags for us. The first is just is this watch point enabled? Yes, no. Uh, the second one is how big of a value are we watching? Are we watching one byte? Are we watching two, four, eight, whatever? Um, then once we have that, the next thing we're gonna do is we're gonna add an import entry. We're gonna import a new JavaScript function. And this function in, this function's purpose is to be called when our watch point is triggered. And it's gonna do the work of alerting us to that fact. And it's gonna, uh, give us a stack trace or something so that we're able to work backwards and say, oh, that watch point was hit at this address by this instruction. Um, as long as this function is only called when a watch point is triggered, the performance impact of this is negligible. Uh, the next thing we're gonna do is we're gonna create a new internal function. As I mentioned before, this is actually a three step process, but we're gonna skip over that now. Uh, and what this internal function is gonna do is it's gonna perform the actual logic of our watch point. So it's gonna do the math of, uh, does this attempted access overlap with, uh, the address we're watching? And if it does, it's gonna call the trigger function, that JavaScript function we just imported. Finally, we have one more step, which is that we're gonna place calls to our watch point function, that internal function we just created, uh, before every single memory load or memory store instruction. Uh, so as long as we're really careful about how many instructions we execute every time we check our watch point, we can actually do this with no noticeable drop in FPS. We can still play video games, we can have all this stuff running in the background, we don't even notice it's there. So now that we understand this process and we've sort of filled in the gaps, what I wanted to do was see if I could sort of emulate cheat engine for WebAssembly. And so I built another tool which I call Cetus, uh, and it is a browser extension that's basically cheat, cheat engine for WebAssembly. Uh, the name Cetus comes from the Latin word for sea monster, so you can probably see the theme I was going for here. Uh, so what Cetus does is it intercepts and instruments WebAssembly binaries in your browser on the fly, uh, before they're executed. And that instrumentation consists of adding read or reading and write watch points. Uh, it adds the functionality to freeze memory, making it so that attempts to change some value in memory don't take. Uh, and it can apply, uh, patches defined by the user. So we can do code patching, change code, uh, through this extent, extension, excuse me. So, uh, with that in mind, uh, I want to get to my first demo if I can figure out how to use this. Okay. So this is just a really simple demo Unity game and our goal here is we're going to make this red, uh, tank here invincible. So the first thing we want to do, uh, is we know that this tank is at 100% health. So we can guess that, uh, it's health is stored as, uh, float value of 100. So we're going to search for that in memory. We, uh, give our value of 100, our comparison of equal as in we're asking the extension, look for all values in memory that are equal to 100 of type float 32. And we get back 112 results. So we still have to narrow it down some. So what we do next is we cause that red tank's health value to change. So we shoot it, it takes some damage. And now what we're going to do is we're going to search for, of all those values we got last time, which ones are less than 100 now? And this narrows it down. We find two of them that are somewhere around 50%, which looks about right, it looks like our tank has something like 50% health. So we've, uh, there's a pretty good chance that one of these two is holding our health value. So what we're going to do is we're going to start with the first one. We're going to, uh, just bookmark it. And from there, we can, uh, start, we've saved the value in memory and we can start to manipulate it. So the first thing we're going to do is we're going to freeze that value in memory. Uh, so now what we see is that when we try to change that value in memory, basically our, our, uh, injected instructions basically say, no, don't do that. We're, uh, this value is frozen. That change doesn't take. Uh, so our tank is now invincible. Uh, so that's, you know, a good start. But again, we want to, we want to make this process a little bit simpler. We don't want to have to do it again. Oh, I'm sorry. I, uh, got a little bit ahead of myself. Uh, so what we do now is we want to set our right watch point. Basically we're asking the extension, uh, tell us what instructions are trying to write to this memory address or trying to, you know, cause the red tank to take damage. And then, uh, we trigger it by, uh, causing that tank to take damage. And in just a second, uh, we get, uh, disassembly of exactly where that code occurred. So we have this function, uh, that, uh, I'll, I'll save you the trouble of reading it. Basically what it's doing is it takes some damage value, it loads our health value, subtracts that damage value from it, and then it stores it, uh, stores the new value in memory as our new health value. So what we want to do here is just take out the part where it's storing that new value. Basically make it so that we can't get hurt anymore. Uh, I should say that this is, this was recorded on a little bit of an older version of CEDA, so there's some graphical issues here. They're worked out now, I promise. Um, but so what we do is we find this F32 store instruction, this instruction that's gonna write to our new memory address, and we just replace it with a couple, uh, other instructions and then we save our patch. And that's pretty much it. Now the one, uh, annoying thing here is that we can't modify code while the binary is running. There's no way to do that within WebAssembly. So what we have to do is we have to restart the game to apply that patch. But as we can see here, we're not using the extension, we're not using CEDIS. Uh, and even still, we can, uh, shoot our new tank and we can see it's invincible. I headed myself here. Don't look. Okay. Um, so, uh, with that being said, I want to talk about a couple other features of CEDIS that I didn't really show, uh, in the demo. But, uh, the first of those is differential searching. So in the example we just showed, we had the benefit of knowing the starting value we were looking for. We knew we were looking for a float value of 100. Uh, that's not always the case though. If you want to talk about like the example of, let's say you're trying to manipulate the location of your player. They're X and Y coordinates or XYZ if it's 3D. Uh, you may not know that your Y coordinate is zero or whatever. But you may be able to say, well if I go up, the Y coordinate goes up. If I go down, it goes down. Left and right effect X. Stuff like that. So differential searching basically lets us find these unknown values, uh, by taking a big slice of all of memory and then saying, okay, uh, what, which of those values increased since the last time we searched? Which ones decreased? Which ones stayed the same? And that way you can narrow it down and find those values without ever actually having to know a starting value. Um, another really fun, uh, feature of CEDENGINE is the built-in speed hack, which is basically used to speed up or slow down games by some multiple. Uh, I tried to emulate this a little bit in, uh, CEDIS. It's actually really easy to do. Basically all it does is it replaces the two functions commonly used to keep track of clock time in your browser and changes the return value by some multiple. And this is typically all you need to do to speed up or slow down a game by whatever, whatever multiple you want. Um, so with that, uh, with CEDIS sort of out of the way, I want to talk about a couple other examples of fun things you can do using whale, using binary instrumentation on, uh, video games. So the first thing that's really useful is you can do function tracing. Uh, this is, uh, pretty simple, it's just injecting some code at the start of each function that says hey, I was called at, you know, I was called, I was called by, uh, this parent function, stuff like that. Uh, this is pretty slow no matter how you do it, but it is pretty useful. Uh, the next thing I want to talk about is replacing functions. So using whale you can, uh, swap out all references to a function, basically replacing it entirely. Uh, so one fun thing you can do is you can import a new JavaScript function and you can replace all references to an internal function with references to that JavaScript function. Basically allowing you to patch a WebAssembly binary with JavaScript. Uh, you do have to be a little careful about performance impacts, but this is still super useful. Uh, this next example is kind of the opposite of that. Uh, so what whale can do is it can take an internal function and export it. Basically make it something that can be called by JavaScript. Uh, so for example, in the case of one particular game, uh, the game would update a player's stat by making an HTTP request to its, its application server. Uh, and the way that it would prevent you from just making the request yourself or editing it on the wire is that it would hash the request and, uh, verify that hash. So the obvious way to deal with this is you just disassemble the binary, find that hashing function, uh, and implement it yourself so that you can, uh, hash your own, uh, request. But it was actually easier to just find that hashing function, export it, and then just call it arbitrarily. Just ask the binary, hey, hash this for me. Uh, and that was a really simple way to, uh, be able to, uh, fake these requests, uh, despite the fact that I didn't have to do any real reverse engineering. Uh, then I got banned. Uh, so, uh, this is less of a video game hack, but, uh, still very useful. Uh, you can add symbols to WebAssembly binaries using whale. Uh, so there's actually two ways that I came, came up with to do this. The first one's sort of obvious. Uh, the WebAssembly specification defines a section specifically for symbols. So like an elf, uh, an elf binary symbol table, uh, the, there's something called the name section in WebAssembly, which is just sort of a list of this name corresponds to this function, etc. Um, uh, another kind of interesting way to do this though was to just add an export entry for every function you want to name. So when you add an export entry to a function, basically when you ask WebAssembly export this function for me, um, you have to give that function a name because that, that name is the variable you're gonna call when you want to call that function from JavaScript. Uh, and just in my experience, pretty much every WebAssembly tool treats the export entry name as the canonical symbol name, uh, for that WebAssembly binary. So this is a really simple way to add symbols into a binary without having to add an entire, uh, entirely new section to the binary. Uh, that sort of covers my examples. Uh, I have one more demo, but before I show it to you guys since I know you, you're all busy, this is where you can find everything. It should all be up right now. Uh, I, I made all the GitHub repos, uh, public right before I walked in here. Hopefully I didn't hit the delete button instead, but if I did, uh, they'll be up in a few minutes. Uh, before I show you this, uh, last demo, I just want to say I, I, uh, had a little bit of trouble figuring out what I wanted to do for a final demo. So I sort of just put together a montage of different game hacks. Uh, so I hope you guys enjoy it. Um, I hope you guys take these tools. I hope you use them and I hope you all get banned from new grounds for doing it. Okay, so this is me invincible in a first person shooter. Uh, this is me no clipping just a little too hard. Uh, this is me throwing infinite grenades through a wall at some poor soul. This one I think is obvious. Uh, this is, uh, just cheating at some sort of pseudo RTS. Uh, as you can see, uh, I have a million of these units. Uh, my money never goes down when I, uh, buy something new. This game actually used a bunch of anti-cheat mechanics. You can't see any of it, but I was kind of proud of this. I don't know what this is. This is the speed hack. Uh, uh, this is sort of my favorite part. Uh, if you've played prop hunt, you've probably seen this before. This is me cheating at prop hunt. Uh, I should be taking damage right now. I'm, I'm the table if you can't tell. And as you'll see eventually these guys get fed up and kill themselves. Thank you very much guys.