00:00:00.000-->00:00:06.417 >>So, we had some AV issues. I had a really cool demo, uh, that was gonna be running on Dolphin 00:00:06.417-->00:00:09.542 Emulator. We're not going to get to see that I don't think, which sucks because I was going to 00:00:09.542-->00:00:13.750 hack Luigi's mansion. Maybe we'll figure it out if we have time. Um, but we're just going 00:00:13.750-->00:00:21.208 to jump in right now. So, this is "XenoScan, Scanning Memory like a Boss." 'Course my remote 00:00:21.208-->00:00:29.250 just stopped working. [laughter] It loves me today guys, sorry. Okay, there we go. So I'm Nick 00:00:29.250-->00:00:35.333 Cano. I'm 24 years old. I wrote the book, uh, "Game Hacking: Developing Autonomous Bots for 00:00:35.333-->00:00:39.917 Online Games." I'm the CEO and lead engineer at XenoBot, which is where I make and sell my own 00:00:39.917-->00:00:46.833 bots for online games and I'm also a senior security architect at Cylance. So Xeno Scan is an 00:00:46.833-->00:00:53.750 open sourced memory scanner and it's written using modern C++ with Lua embedded. It's highly 00:00:53.750-->00:00:59.375 extensible and I wrote it with like, advanced users and automation in mind. A lot of 00:00:59.375-->00:01:04.083 memory scanners out there don't have that, so that's kind of what I targeted with this. So 00:01:04.083-->00:01:09.167 you might say "Okay wait, what's a memory scanner? Is it this thing?" No, that is not a memory 00:01:09.167-->00:01:13.917 scanner. As cool as that looks... I am not that cool. So, this is a memory scanner. 00:01:13.917-->00:01:19.417 Basically, what a memory scanner does is you start a scan with some initial criteria, then you 00:01:19.417-->00:01:24.125 run the scan and it will scan the memory of a remote process looking for anything that 00:01:24.125-->00:01:28.500 matches that criteria. If you found your target, which general means you have one or two 00:01:28.500-->00:01:33.417 addresses, then you're good to go. You're done. If not, you keep updating your criteria to 00:01:33.417-->00:01:38.542 reflect changes in the game and then you keep scanning until you've narrowed down the list to 00:01:38.542-->00:01:43.500 something you want. So in practice that looks something like this. We have a game and we 00:01:43.500-->00:01:50.250 have 500 health. So we set our criteria to 500 and uint32 let's say that's the type of health. 00:01:50.250-->00:01:56.375 And then we do a scan and we get these results back. We have like 12345 results or whatever that 00:01:56.375-->00:02:01.625 are equal to 500. Obviously that's probably too many. So then we'll change our health in 00:02:01.625-->00:02:07.042 the game to 450 and when we do that, those values will change accordingly and then we can run 00:02:07.042-->00:02:12.667 a new scan and narrow them down. Some of the values changed to 450 as well. Some didn't. Some 00:02:12.667-->00:02:16.875 stayed 500. But we narrow it down to the ones that are now 450. Now we still don't have 00:02:16.875-->00:02:21.250 enough. We still have a couple hundred so we'll set up our criteria again to where we heal 00:02:21.250-->00:02:26.667 in the game and now we have 550 health. So we'll set that criteria, scan – boom, we have 00:02:26.667-->00:02:30.625 our final value, our health, we're done. So in practice, that's how a memory scanner 00:02:30.625-->00:02:34.917 works and that's what all of the memory scanners that a lot of you have probably heard of, or 00:02:34.917-->00:02:38.833 seen before, do. I take it a step beyond this, but I just wanted to get this out of the 00:02:38.833-->00:02:45.125 way to say "this is what we have" and we're going to look at what I've added and what I 00:02:45.125-->00:02:51.333 haven't seen before that I think is cool. So you might be asking "Why another memory scanner?" 00:02:51.333-->00:02:56.083 Well, uh, existing ones, they just don't cut it. So their- their source is either closed 00:02:56.083-->00:03:00.583 and you can't look under the hood, you can't add your own stuff, or they are extremely 00:03:00.583-->00:03:06.292 messy, hard to understand, outdated and they're lacking a lot of easy to make features. So 00:03:06.292-->00:03:10.292 there's a lot of stuff that you can write scripts for in existing memory scanners, but 00:03:10.292-->00:03:13.417 sometimes you don't want to write a script to do something. You just want it to be there for 00:03:13.417-->00:03:17.417 you and that comes from their lack of vision really. They don't sit down and say "How can 00:03:17.417-->00:03:22.083 I make this better for people to use?" And they're designed for beginners. Like Cheat Engine is, 00:03:22.083-->00:03:27.333 like, point and click. You just put in the number, hit next, keep scanning, you have your 00:03:27.333-->00:03:31.333 values, but you can't really do much with it. You can't automate it. Which is- if you're hacking 00:03:31.333-->00:03:36.000 games at a very high level, you want to be able to automate the scanning. You want to be able to 00:03:36.000-->00:03:40.000 throw something open and run scripts against it to find memory and stuff like that. So I 00:03:40.000-->00:03:46.042 went with a focus on that. And so, why am I telling you? Like, why do you care? Well, I think 00:03:46.042-->00:03:50.542 memory scanners are cool and very powerful, even if you're not hacking games, and we'll get 00:03:50.542-->00:03:55.083 to that at the end. They can show you how to work with raw memory at the lowest, deepest 00:03:55.083-->00:03:59.542 levels and there's, like, a very specialized set of, like appsec skills that you need to 00:03:59.542-->00:04:04.208 understand them and make them. Knowing how to pull memory from other processes without crashing 00:04:04.208-->00:04:08.375 them. Knowing how to tell different types of memory apart. Whether it's readable or not 00:04:08.375-->00:04:12.333 readable, and all that cool stuff you'll have to do. So we're going to jump into a 00:04:12.333-->00:04:17.167 technical overview and just look at what the code looks like and then we'll get to some demos 00:04:17.167-->00:04:22.958 that I hope work. So the languages and tool chain are cmake, C++, LuaJIT, and Git. Git 00:04:22.958-->00:04:24.958 for source control, cmake for building projects and then C++ with embedded LUA using LuaJIT, 00:04:24.958-->00:04:26.958 which is just a just in time compiler. You can see two scripts here. The top one, if 00:04:26.958-->00:04:28.958 you're compiling on Windows with Visual Studio, you do that, you have a Visual Studio solution, 00:04:28.958-->00:04:30.958 you'll compile, everything is done. Compiles LuaJIT and everything. If you're on another 00:04:30.958-->00:04:32.958 system, you have to do- might have to do something, like, on the bottom. You probably don't 00:04:32.958-->00:04:36.667 have time to, like, memorize this but you can get the slides afterwards, so... And- it's- 00:04:36.667-->00:04:41.958 there's no user interface. So instead of having a user interface where you're gonna 00:04:41.958-->00:04:46.958 click through, you're gonna write code to do your scans. And this code just reflects the scan 00:04:52.375-->00:04:57.375 that we went through with the diagram a few slides back. It scans for 500, then 450, then 00:05:05.625-->00:05:10.625 550, waiting for user input in between each scan. Um, and then it just takes the results, 00:05:14.917-->00:05:21.500 serializes them to a table, prints them out and you're done. So the code is architected into 00:05:21.500-->00:05:27.792 three main projects. There's the project you can see on the left here called XenoScan Engine. 00:05:27.792-->00:05:32.792 That's the core scanning code. That's the heart of the scanner. That's what knows how to search 00:05:32.792-->00:05:37.792 for certain types of values. Knows how to flip in DNS. Knows how to search for chunks and 00:05:37.792-->00:05:43.208 thread all of that. All the fancy stuff is there. It exposes three things: uh, scan variants, 00:05:43.208-->00:05:49.250 scan target, and a scanner. A scan variant just wraps any type that might- you might be 00:05:49.250-->00:05:54.583 scanning for, so, integers, strings, and everything. Whereas the scanner target is the 00:05:54.583-->00:05:59.250 wrapper for what you're scanning. So you can scan a windows process, you can scan a 00:05:59.250-->00:06:03.667 game running in an emulator, uh, there's a lot of different things you'll be able to scan in 00:06:03.667-->00:06:07.583 the future as that is extended. We'll get to that as well. And then the scanner that's exposed 00:06:07.583-->00:06:12.167 is what actually does the scanning. And the project all the way on the right, XenoLua, 00:06:12.167-->00:06:16.833 is just a wrapper around Lua. Lua's kind of old school, written in C, not really easy to 00:06:16.833-->00:06:21.750 use, so what I've done is write a wrapper around that that has a variant system and everything to 00:06:21.750-->00:06:25.333 make it easier to use. And then in the middle you have XenoScanLua which ties 00:06:25.333-->00:06:30.917 everything together like so. There's C++ code doing object translation of the scanner 00:06:30.917-->00:06:35.417 targets and the scanner, as you can see with the orange lines. And then there's type 00:06:35.417-->00:06:41.458 translation of the variants. And that C++ level translation then goes and talks to XenoLua and 00:06:41.458-->00:06:46.458 exposes these objects and types to Lua. Now you can see these green connections. The dotted 00:06:49.125-->00:06:54.125 green connections are kind of, um, they're like indirect connections through Lua so you 00:06:54.125-->00:06:58.792 can see those on either side of the XenoScanLua. What that means is once the values are pushed 00:06:58.792-->00:07:03.833 from C++ into Lua, there's then a Lua library that wraps them and makes them even cleaner to 00:07:03.833-->00:07:08.000 use. Those are those dotted lines. Then the green lines are the scripts the user will be 00:07:08.000-->00:07:12.000 running and the library itself and they have a green line joining them which means they 00:07:12.000-->00:07:16.417 can talk to each other. And those are running in pure Lua code, which is what is user 00:07:16.417-->00:07:20.042 phasing. If you're not writing code on the scanner, you're just using the canner, you're going 00:07:20.042-->00:07:24.458 to be writing in Lua, which is really ubiquitous in the world of gaming, both for game hacking 00:07:24.458-->00:07:31.375 and actually game dev. So I think- I think it was a good language choice here. So we have 00:07:31.375-->00:07:37.167 general, basic scanning functionality. Uh, we support all the types. So integers of 00:07:37.167-->00:07:39.167 all bitnesses from 8 to 64, both signed and unsigned, as well as 32 bit floating point, 64 bit 00:07:39.167-->00:07:41.167 floating point and strings that can both be single byte or multibyte. And it just- there's 00:07:41.167-->00:07:43.167 also composable types. So instead of just saying "I want to scan for this integer," you 00:07:43.167-->00:07:45.167 can say, "Well I've torn this part- this game apart before. I know exactly what the structures 00:07:45.167-->00:07:49.375 look like." And what I'm doing now is I'm automating the process of launching the new 00:07:49.375-->00:07:51.875 version of the game, putting in known values and then scanning for those, to find the new 00:07:51.875-->00:07:54.250 addresses where that stuff is located. So you might make a structure for that that reflects 00:07:54.250-->00:07:57.208 what the actual structure of the thing is in that game. And then you can fill in all the values 00:07:57.208-->00:07:59.458 and it will search for the entire structure. And really what's beautiful about this is, 00:07:59.458-->00:08:02.417 y'know, 500 might appear in the game a lot of times and 600 might appear in the game a lot 00:08:02.417-->00:08:07.417 of times, but how many times do 500 and 600 appear sequentially back to back, right next to each 00:08:09.417-->00:08:14.417 other. A lot less. So as you make a structure and you compose it, and you set up all your 00:08:44.958-->00:08:49.625 values, you narrow the margin of error when scanning. Taking it from having to do, like, 5 00:08:49.625-->00:08:54.625 rescans, 6 rescans, to one. So we're gonna try to do the demo here. So the demo is just this 00:08:57.583-->00:09:02.625 code right here scanning against the little video game that it's going to pop up. And I don't 00:09:02.625-->00:09:06.333 have the code open like I planned to because I had to switch laptops. If everything 00:09:06.333-->00:09:11.167 goes as planned, what's going to happen, it's going to scan for a structure doing one scan like we 00:09:11.167-->00:09:16.417 just looked at and if it finds the right address it's gonna overwrite the x and y value of 00:09:16.417-->00:09:20.750 the little object in this game to the finishing value and the game should say "Okay, you're a 00:09:20.750-->00:09:25.750 winner." So I'm just gonna run that really quick. One sec. So you can see your game there. 00:09:48.458-->00:09:52.583 When I hit a button it's gonna start the scan. Ah, you won't be able to see it because I can't 00:09:52.583-->00:09:56.833 move everything over to that screen as planned, so sorry about that. So I'm starting the 00:09:56.833-->00:10:03.167 scan now. It's going to search for the exact same values we just saw, or it's going to 00:10:03.167-->00:10:08.417 freeze on me... Oh, I'm in the wrong window. Okay running the scan. We're winner. You can see 00:10:08.417-->00:10:12.500 the green box move to the bottom right there for a second. That was it moving from its starting 00:10:12.500-->00:10:15.750 position to the finishing position. Obviously this is a really contrived game that I 00:10:15.750-->00:10:21.292 threw together just for an example, but it works. And now we need to try and close it when 00:10:21.292-->00:10:26.292 we can barely see. Cool. So on top of that I have put ranges in so that if you don't know 00:10:30.500-->00:10:35.667 exactly what a value is, but you know- you know it must be between 500 and 1000, then you 00:10:35.667-->00:10:41.000 can just "Okay, between 500 and 1000." And you can do that by either specifying the type and 00:10:41.000-->00:10:45.000 then inside of the type function putting the range function and the type system will sort that 00:10:45.000-->00:10:49.667 out for you. Or you can say "I have a range" and then put the type first and then put the two 00:10:49.667-->00:10:54.708 values and it will sort that out for you. If you're doing it in a strongly typed way such as a 00:10:54.708-->00:10:58.375 structure where you've already defined all the types you don't even have to do any of that as 00:10:58.375-->00:11:02.833 you can see in the bottom code block you just say "equals range" and it's going to figure 00:11:02.833-->00:11:09.167 out the types automatically. There's also placeholders. So think of this as like a wild 00:11:09.167-->00:11:14.917 card, uh, like dotstar if you're familiar with RagX, um, where you just put in these empty 00:11:14.917-->00:11:19.333 brackets which is an empty Lua table and what that's going to do- it's going to say "I know 00:11:19.333-->00:11:23.250 this is part of the structure and it takes up this much size but I don't know the value. So 00:11:23.250-->00:11:27.125 when you hit this part of the structure in scanning accept anything, move on the the next 00:11:27.125-->00:11:32.125 thing." So that might look like this. Um, this is the same as before but our stage is now set 00:11:35.500-->00:11:41.750 to a placeholder. Our level is the same. X is the same., but Y is now a range and max health is 00:11:41.750-->00:11:47.042 now a range. And if we run that demo again, what we should see is that it still works, uh, with 00:11:47.042-->00:11:52.042 those changes. So I'll go over this really quickly. So we have that little green square on the 00:11:56.958-->00:12:00.708 left. That green square needs to move to the yellow square to win. I forgot to say that last 00:12:00.708-->00:12:05.208 time. So we're going to do the same scan as before and then we're gonna pull the first 00:12:05.208-->00:12:10.958 result and overwrite the x and y values of that first result. That's all we're going to do. 00:12:10.958-->00:12:17.250 And what should happen there, is it should go to the end as it did before. The thing is, is the 00:12:17.250-->00:12:20.250 first result going to be right? Because now our structure doesn't have the constraints 00:12:20.250-->00:12:25.333 that it has before- that is had before because we have ranges in there. It allows for more error 00:12:25.333-->00:12:31.625 in there. But if we run it, we see that we still win because even though we lowered our 00:12:31.625-->00:12:36.292 constraints, the constraints with the structure there were still enough to find exactly 00:12:36.292-->00:12:39.708 what we were looking for even though there's all these graphics going on and all this 00:12:39.708-->00:12:44.708 drawing and all this other state in the game that might just happen to look similar. Sorry. I 00:12:47.625-->00:12:52.833 was supposed to be at this slide when I did that. So- and there's variable type to values as well. 00:12:52.833-->00:12:59.250 What I mean by that is you don't have to use strong types like we've been talking about. You 00:12:59.250-->00:13:03.458 could specify typemode exact which is what we've been talking about and is only what is 00:13:03.458-->00:13:08.167 supported in structures 'cause if you try to do variable size types in structures, the 00:13:08.167-->00:13:13.542 complexity just blows up. 'Cause you have to generate a new instance of that structure for 00:13:13.542-->00:13:18.667 every combination of every weak type you have in there. And then you end up with, like, five 00:13:18.667-->00:13:23.583 hundred thousand different ways the structure can look and it just blows up. Can't sustain. 00:13:23.583-->00:13:28.500 But for everything else if you're just scanning for a value you can say "typemode tight" 00:13:28.500-->00:13:33.500 which will do other values of similar types. Soi if you say- if you just put 500 in there and 00:13:35.917-->00:13:42.458 you say "typemode tight" it's gonna do all integer types and floating types. If you do 00:13:42.458-->00:13:46.417 typemode exact it's only going to do the integer type you specify. If you do typemode 00:13:46.417-->00:13:50.958 loose and you put 500 in there, it's going to search for 500 as any integral type of any 00:13:50.958-->00:13:57.333 signedness or floating point types or strings that say 500 'cause why not. Not sure why 00:13:57.333-->00:14:02.125 you'd want that, but we have it anyways. So- and if you search for a string with typemode tight 00:14:02.125-->00:14:07.125 it will search for it both as wide or single byte. So. Now this is the part of the talk 00:14:11.917-->00:14:15.917 that- this is what I really want to talk about. 'Cause right now we've gone over generic memory 00:14:15.917-->00:14:21.000 scanner stuff. If you've hacked games before, everything I just told you, you're like "oh that's 00:14:21.000-->00:14:25.125 cool." The structures are cool, but you can write a script for that. There's not really 00:14:25.125-->00:14:29.167 anything new. Why the hell are you here? And this is what I want to talk about is data 00:14:29.167-->00:14:34.875 structure detection. So in my book I showed some Cheat Engine scripts to do this and after I 00:14:34.875-->00:14:40.083 wrote the book I was like, wait, why do I have to write a Cheat Engine script to do this? Why 00:14:40.083-->00:14:45.750 don't I have a memory scanner that does this? So that's what I set out to do. So we have to 00:14:45.750-->00:14:50.875 make a few assertions first. The first assertion is that a data structure is just a complex 00:14:50.875-->00:14:54.667 interconnected web of pointers. Just pointer pointing here, pointing there, pointing there, 00:14:54.667-->00:15:01.000 pointing there. But each data structure has very specific characteristics and shapes and 00:15:01.000-->00:15:05.083 if we can detect those characteristics and detect those shapes we can detect the entire 00:15:05.083-->00:15:10.083 data structures. Furthermore, a process is just a massive super complex data structure. So if we 00:15:12.292-->00:15:17.208 take the web that is that process and try to sort through it, then we can find all of the 00:15:17.208-->00:15:23.958 data structures which it contains. So I said in the last slide- I said hypothetically you 00:15:23.958-->00:15:27.667 could do this, but it's not hypothetical anymore because I've done it and it looks 00:15:27.667-->00:15:32.375 something like this. So you can see on the top left, first we're going to scan for all of the 00:15:32.375-->00:15:37.417 valid pointers in the process we're attached to. Now what I mean by a valid pointer is 00:15:37.417-->00:15:43.250 anything that is a value that looks like a value for valid memory space within that 00:15:43.250-->00:15:48.958 process. So it could be a value that's not a point and by chance hits valid memory space, but 00:15:48.958-->00:15:51.792 we're going to use it anyways because we can't tell the difference. And we're going to 00:15:51.792-->00:15:56.000 organize those results into a pointer map. The pointer map is this weird thing you can see 00:15:56.000-->00:16:01.958 here, bottom left. So you can think of it as like, just a map of key value pairs where the key 00:16:01.958-->00:16:07.750 is a pointer and the value is a list of pointers that point back to that pointer. So if you take 00:16:07.750-->00:16:13.625 the top example here. We have Ox00010000 and it fans out up into the right and shows all the 00:16:13.625-->00:16:18.625 values that point to that pointer. So once we have these maps, we can then begin to walk 00:16:20.833-->00:16:25.417 them and look at the characteristics of each pointer in this map and see if it looks 00:16:25.417-->00:16:30.292 like a data structure. And if it does, well, we found our data structures right? So for each 00:16:30.292-->00:16:36.917 one we find, we add it to our results map, and then we're done. Theoretically. So we're 00:16:36.917-->00:16:41.917 going to talk about class instances 'cause this is the most simple example of data 00:16:41.917-->00:16:46.292 structure detection 'cause really a class instance is just a data structure. Um, the 00:16:46.292-->00:16:50.417 assertions we have to make here are that we're talking about abstract classes where there's a 00:16:50.417-->00:16:55.417 virtual function table as a first member of the class and that's basically it. So if we 00:16:58.250-->00:17:04.875 want to find a class like this, um, you see here this blue box? That's what we're searching for. 00:17:04.875-->00:17:10.458 That's the pointer we want. All the way on the left. The blue box needs to be a non-module 00:17:10.458-->00:17:15.250 pointer. If it's a pointer that falls within the address space of a module, meaning if it's a 00:17:15.250-->00:17:19.375 pointer that's in code then it could just be assembly code that's moving a VF table into 00:17:19.375-->00:17:24.417 place. Assembly code isn't a class instance so it's important to make sure it's a non-module 00:17:24.417-->00:17:30.833 pointer. And this pointer then needs to point to another pointer in read only memory. 00:17:30.833-->00:17:35.417 That's important because a virtual function table is typically going to exist in read 00:17:35.417-->00:17:42.167 only memory somewhere in the P-header of the module that, uh, instantiates that class. So once 00:17:42.167-->00:17:46.292 we have that something pointing to a read only pointer that's a non-module pointer that read 00:17:46.292-->00:17:51.083 only pointer should then have an element there that points to an executable pointer which is 00:17:51.083-->00:17:55.292 indicative of a function. So if we have something in read only memory pointing to a function 00:17:55.292-->00:18:00.292 being pointed to by memory on the heap then it's probably a class instance with a VF table. 00:18:02.375-->00:18:06.625 So this is what the code actually looks like to do that once we have the pointer map, 00:18:06.625-->00:18:10.917 uh, you can't really take much from it but it can fit in a slide so I wanted to show how 00:18:10.917-->00:18:17.042 like- how condensed it is once it's actually written out. And we're gonna do a demo of that. 00:18:17.042-->00:18:22.042 Uh, let me open the code really quick 'cause I don't have it open anymore. Oh my god. I don't 00:19:08.250-->00:19:14.333 have the scroll wheel. Okay, I don't know if you guys can read that well but let's just go 00:19:14.333-->00:19:18.875 through it really quickly. So we're gonna declare a class base. This base is going to have 00:19:18.875-->00:19:24.583 a virtual function called "dostuff" and it's gonna have a type field that is a uint32. 00:19:24.583-->00:19:30.042 Then we're going to implement base as one class called "NOP." That NOP class will set it's 00:19:30.042-->00:19:35.875 type to NOP. Which you can see at the top in the enum is Ox90 and it's dostuff function will 00:19:35.875-->00:19:40.667 do nothing, which is really not characteristic of a dostuff function but that's what it 00:19:40.667-->00:19:46.000 does. Cool. So then we have another class instance called "win." It's dostuff function 00:19:46.000-->00:19:52.083 does something different and that is print "I am a winner." And it's type is typewin which 00:19:52.083-->00:19:57.083 if we scrolled up we'd see is Ox1337. So what we're gonna do is we're gonna run a class 00:19:59.375-->00:20:04.375 instance, uh, data structure scan which looks like this. So the first thing we're gonna do 00:20:21.708-->00:20:26.000 is we're gonna find all class instances after running the processes and attaching to it. 00:20:26.000-->00:20:29.833 Once we've found all class instances, we're just going to loop through them. We're going 00:20:29.833-->00:20:35.250 to find one whose type is Ox90. That's the instance of our NOP class. When we find that we're 00:20:35.250-->00:20:38.667 just gonna store its address. We're going to do the same thing for our win class which is 00:20:38.667-->00:20:43.917 Ox1337. And then we're gonna come down here and we're going to overwrite the virtual 00:20:43.917-->00:20:48.750 function table of the NOP instance with the win instance. Now what that's going to do, if 00:20:48.750-->00:20:55.208 we look at our C++ code again, what's happening is we're going to instantiate an instance of 00:20:55.208-->00:21:01.917 each class and call the function "play" which you can see here with NOP. And the play function 00:21:01.917-->00:21:05.542 right about the main function, what it's going to do, it's just going to loop forever and it's 00:21:05.542-->00:21:10.542 gonna say instance dostuff. So our instance of NOP should do nothing when dostuff is executed 00:21:12.667-->00:21:16.708 but our instance of win should print stuff. And as you can see we're using a NOP instance. So 00:21:16.708-->00:21:22.000 if the scanner works it's gonna scan this process, replace the virtual function tables, and the 00:21:22.000-->00:21:27.000 NOP class instance will start acting like the win class instance does. So this the 00:21:44.292-->00:21:49.875 window of the program we just looked at in C++ and when I run the scan, if everything works 00:21:49.875-->00:21:53.750 out, this should start spamming "I am a winner." Right now it's just looping and calling a 00:21:53.750-->00:21:58.750 dostuff function that just returns and does nothing. Cool. [applause] I haven't tested 00:22:07.167-->00:22:12.167 anything on this laptop and that was a scary one. So let's- let's get a bit more complex. Class 00:22:17.500-->00:22:22.125 instances are pretty basic. There's three things we have to look for but what- let's- let's 00:22:22.125-->00:22:26.625 look for something crazier like a linked list. And let's look at the STD list implementation just 00:22:26.625-->00:22:31.792 because that's pretty commonly used in games since they're written in C++. So as before the 00:22:31.792-->00:22:36.167 blue box on the left is what we're looking for and that's a pointer. This pointer must be 00:22:36.167-->00:22:41.000 followed by an integer N. That integer N we're gonna get to in a minute. That pointer needs to 00:22:41.000-->00:22:46.750 point to something called a node, which you can see just to the right of it. Now a node has 00:22:46.750-->00:22:51.917 two definitions. The first definition, branching off to the bottom, is that it must point 00:22:51.917-->00:22:57.208 forward to something which, when that pointer is incremented, points back to it. Because on a 00:22:57.208-->00:23:00.625 duly linked list you have a forward pointer and a back pointer. So it should point to 00:23:00.625-->00:23:04.875 something, which points back to it, and it should point back to something, so when you increment 00:23:04.875-->00:23:09.500 the pointer that is that node, it should point to something that points back to it again. If 00:23:09.500-->00:23:13.958 it does that we know it's a node. The next criteria for a node, which are only going to 00:23:13.958-->00:23:20.250 apply to the first node we find, is that if you keep walking the forward pointer as you see here 00:23:20.250-->00:23:24.750 on the right, you just keep going forward, you should eventually end up back at the 00:23:24.750-->00:23:30.542 node you started with because these are circular lists, right? And the amount of steps it 00:23:30.542-->00:23:35.292 takes, the amount of nodes you iterate over, before you get back to your original node, is 00:23:35.292-->00:23:41.000 that integer N we talked about. So what we're looking for is a pointer followed by an integer N 00:23:41.000-->00:23:47.625 such that that pointer points to a node which when walking the node- the next chain, that N 00:23:47.625-->00:23:52.625 follows the pointer. Okay so this demo's pretty simple. The C++ code is just we're going to 00:24:10.292-->00:24:15.292 declare a list of uint32 and we're gonna fill it with every value from 0 to 100 that is an 00:24:17.583-->00:24:21.542 integer including zero itself and then in a while loop we're just going to sum all those 00:24:21.542-->00:24:27.708 things. So the sum of everything from 0 to 100 is 5-->050 (speaker misspoke). So if the value is 00:24:27.708-->00:24:32.708 not 550, we're going to print "Oh no. Somebody modified our list." So the code looks like 00:24:55.000-->00:25:00.000 this. We're gonna look for all instances of STD list after running the application, then 00:25:00.000-->00:25:05.500 we're gonna loop through them and look for a list which has an item count of 101, because we 00:25:05.500-->00:25:11.208 have every value from 0 to 100, so there's 101 values. Once we find, that we're just gonna 00:25:11.208-->00:25:16.083 store the list header in a variable and then we're gonna move on. We're gonna take that 00:25:16.083-->00:25:20.500 list header, we're gonna read memory from it, which is gonna give us a pointer to our first 00:25:20.500-->00:25:25.500 node. Once we have the pointer to our first node we're gonna overwrite the memory 8 bytes 00:25:25.500-->00:25:30.500 past the start, so at 0 to 4 is the pointer to the next node, at- or 0 to 3- at 4 to 7 is the 00:25:33.625-->00:25:38.125 pointer to previous node, so starting at 8 is the value in that list node. So if we 00:25:38.125-->00:25:43.750 overwrite the value at 8 with a uint32 of one we've just changed 0 to 1 and the program should 00:25:43.750-->00:25:48.750 start saying that something modified the list. So this our window and as you can see it's 00:26:03.375-->00:26:08.125 behaving normally, which is that it's doing nothing, because the list is summing to exactly what 00:26:08.125-->00:26:14.542 is expects the list to sum to. But if I go ahead and run the scan we're able to pull all 00:26:14.542-->00:26:19.542 lists out of memory, find the one just based on its size, no other information, and then 00:26:19.542-->00:26:24.542 overwrite a value in it and completely change the behavior of the program. [applause] Thank 00:26:28.292-->00:26:33.292 you. Okay. So now we're going to move on to the one that really gave me hell. So up until 00:26:39.958-->00:26:44.750 recently, when I've moved and got worse internet, this project was completely done on a live 00:26:44.750-->00:26:49.458 stream where I stream myself coding, small webcam in the corner, stream the dev and I 00:26:49.458-->00:26:55.000 could talk to people and tell them why I'm doing stuff. WHen coding this on the live stream, 00:26:55.000-->00:26:59.958 I was completely unresponsive to anyone in chat. I was melting into my hands. I was trying to 00:26:59.958-->00:27:03.500 figure it out. I actually had some screenshots but there would have taken up too much time. But 00:27:03.500-->00:27:09.375 this one really messed with my head, 'cause you can know the theory, you can know exactly 00:27:09.375-->00:27:16.000 what a binary tree looks like, right? You can know exactly how the pointer should interact, but 00:27:16.000-->00:27:20.292 since the structure's so complex you're gonna end up in these loops and these chains of 00:27:20.292-->00:27:24.708 pointers which look like binary trees but aren't and loop infinitely until you run out of 00:27:24.708-->00:27:31.167 stack space. ANd it was really a pain. So I consider myself a pretty good coder and if I could 00:27:31.167-->00:27:35.583 barely get this code working, I definitely can't get a diagram made for it because I'm terrible 00:27:35.583-->00:27:41.875 at making diagrams. So we don't have a diagram like before. We have all of this text. What it 00:27:41.875-->00:27:48.375 boils down to is that an STD map, which is a binary tree underneath, can be defined as 00:27:48.375-->00:27:53.375 something that has a parent node or a root node and that node has a parent which, when you point 00:27:55.500-->00:28:00.750 to its parents parent, is itself. So the root node is basically like Fry from 00:28:00.750-->00:28:05.375 Futurama. It's its own grandparent. If you find a node that is its own grandparent, you 00:28:05.375-->00:28:10.333 know that's the parent node. Then you want to walk every single node from the parent node 00:28:10.333-->00:28:15.375 left and right. So each node has a left pointer, a right pointer, and a parent pointer. You want 00:28:15.375-->00:28:20.583 to walk every discoverable pointer from something that seems to be a valid root node 00:28:20.583-->00:28:26.000 and it must- each one must follow some criteria. Now there's a lot of criteria here. 00:28:26.000-->00:28:31.208 For instance, there can be a one node tree which is just a root node by itself and you can see 00:28:31.208-->00:28:36.125 this second line of code where the first indented line of code shows how we detect that. The 00:28:36.125-->00:28:41.292 node can also be in a two node tree that is left heavy. That means we have our root node and 00:28:41.292-->00:28:45.458 then it points to the left side to one more node. That node then is going to point back to the 00:28:45.458-->00:28:50.333 root node on its left and right to mark that it's the end of the tree. And you could also have a 00:28:50.333-->00:28:55.583 right heavy two node tree which is the same thing but on the right side. In addition, you can 00:28:55.583-->00:29:00.875 have a node that's at the bottom of a tree, so each side points back to the root node but it's 00:29:00.875-->00:29:06.542 parent is not the root node. All of that's defined here. Or you could have a regular node. And a 00:29:06.542-->00:29:11.375 regular node is defined as something which has a parent which points back to it on 00:29:11.375-->00:29:16.375 either the left or the right and it has at least one left or right that is not the root node, 00:29:19.042-->00:29:22.167 because if both left and right pointed to the root node it would be at the bottom of the 00:29:22.167-->00:29:27.167 tree. Um, so if actually any two of those three are correct,then it's just a normal node. So then 00:29:29.625-->00:29:34.792 the tree is valid if a root node exists and if every discoverable node from the root node is a 00:29:34.792-->00:29:40.167 valid node, which we've just discussed, but on top of that if every discoverable node is 00:29:40.167-->00:29:44.875 unique. So if we walk the tree how you expect to walk a tree, which is recursively left right 00:29:44.875-->00:29:50.000 left right left right until you hit the root node at the bottom of each side, no node should be 00:29:50.000-->00:29:54.000 discovered twice. There should be no looping pointers and if there is that's gonna get you 00:29:54.000-->00:29:59.500 into this crazy loop where you loop forever and it runs out of memory and crashes your memory 00:29:59.500-->00:30:06.000 scanner. So that's not good. So we're just gonna look at what this actually looks like. So I 00:30:06.000-->00:30:11.000 think you guys can see this pretty well. We're gonna define a map where the key is a 32 bit 00:30:20.167-->00:30:25.167 integer and the value is a string. Then we're gonna fill that map consecutively with 00:30:30.542-->00:30:35.542 integer keys 1 through 5 and string values 1 through 5 such that the strong names of the 00:30:40.208-->00:30:46.708 numbers are the value associated with their integer key. Then we're just gonna loop through 00:30:46.708-->00:30:53.042 it, clear the screen, and then just print the entire contents of the map out to, uh, the 00:30:53.042-->00:30:58.042 console and then sleep for a bit and do that again. I might actually increase the text size 00:31:07.208-->00:31:12.208 on this window really quick so that it can be seen better. Or not 'cause my console's being 00:31:31.417-->00:31:38.042 dumb. Sorry. So I'm really not sure- I guess you probably can read this and it's doing exactly 00:31:38.042-->00:31:43.625 as we said. It's printing out the value followed by its key so we have one one, two two, and so 00:31:43.625-->00:31:48.625 on all the way to five. So if we look at our code what we're gonna do is we're gonna search 00:31:59.458-->00:32:04.333 for all map instances and then we're gonna loop through all of the found map instances until we 00:32:04.333-->00:32:09.750 find one which has a length of five. Once we've found one with a length of five, we're then 00:32:09.750-->00:32:14.625 going to store that pointer and then we're gonna iterate over it, just walking the tree left 00:32:14.625-->00:32:20.542 and right recursively and we're gonna rewrite the nodes with some other text, uh, then what 00:32:20.542-->00:32:25.542 they currently have so, let me find my mouse. So the window's up there so I'm going to run the 00:32:28.292-->00:32:33.292 script now. So we replace the text, uh, even though we're using an STD string inside of an 00:32:39.667-->00:32:44.667 STD map we were able to just know that it's in an STD map, know its size, and then go in 00:32:49.125-->00:32:55.208 there and completely replace the text even though it's running in another application. [applause] 00:32:55.208-->00:33:00.625 So now we're going to get to extensibility. And I'm- this is sad because I have ten minutes 00:33:00.625-->00:33:07.083 left which means I have no way to get this demo working, but one of the points of this memory 00:33:07.083-->00:33:12.833 scanner was that I wanted it to be extensible. I don't want a memory scanner where, oh, a new, 00:33:12.833-->00:33:17.083 like, games are starting to pop up on Linux or games are starting to pop up on Mac or 00:33:17.083-->00:33:21.292 Android games just became a thing. Well you can't use Cheat Engine on it until the guy 00:33:21.292-->00:33:26.542 updates it, however long, right? I want it to be so just a few lines of codes and this can run 00:33:26.542-->00:33:31.208 on anything and not necessarily memory. It's a memory scanner but when I went into this I 00:33:31.208-->00:33:36.500 realized it doesn't need to be just a memory scanner. So we have this interface, you have 00:33:36.500-->00:33:41.583 four public functions, two protected functions, and a couple of protected variables. 00:33:41.583-->00:33:46.333 Anything that you can implement this properly for, it can scan 'cause all of the scanner logic 00:33:46.333-->00:33:51.333 lives outside of this and relies on only these functions and values. So the example that I've 00:33:53.708-->00:33:59.458 done here is I've taken Dolphin Emulator, which runs GameCube and Wii games, and I've put in 00:33:59.458-->00:34:04.708 Luigi's Mansion and I've implemented this for Dolphin. You'd see a demo right now but 00:34:04.708-->00:34:09.333 it's not here. Basically what I've done is I've patched Dolphin to have a shared memory 00:34:09.333-->00:34:14.042 segment where it allocates all the memory for the game and then I acquire that shared memory 00:34:14.042-->00:34:18.708 segment in my implementation of that interface and then scan through it knowing like, what 00:34:18.708-->00:34:23.958 the mapping looks like. We can find stuff. So if I had the demo here the first thing I'd do is 00:34:23.958-->00:34:29.208 show Luigi losing health and then I'd freeze the health at 100 and you'd see he won't lose 00:34:29.208-->00:34:33.875 health anymore and then I'd freeze it at 101 'cause why not? If we can do 100 we can do 101. 00:34:33.875-->00:34:37.750 And he'd have these wings that were like hearts, just hearts flying out of his back looking 00:34:37.750-->00:34:42.333 like angel wings because that's the animation the game makes when you lose health and it 00:34:42.333-->00:34:46.417 keeps trying to decrease his health from 101 but the scanner's overwriting it with 00:34:46.417-->00:34:52.000 100 every time. So it kind of looks funny to play on. And then I was going to change it to 1337 00:34:52.000-->00:34:56.167 and when it gets that high the screen completely trips out. There's a little heart in the 00:34:56.167-->00:34:59.917 corner that's supposed to be your health. It grows to the size of the screen and it's just 00:34:59.917-->00:35:04.417 beating and there's like this pink flashing going on like I guess Luigi was like "I have so 00:35:04.417-->00:35:10.375 much f***ing health. I'm gonna go rave." So that would have been great and I figured that 00:35:10.375-->00:35:14.917 would be a demo that would carry this talk, but sadly we don't have it. I'll put a video online 00:35:14.917-->00:35:19.917 with the slides, uh, afterwards. So future work. I want to add a lot of stuff. So targets for OSX 00:35:23.083-->00:35:26.333 and Linux where we just implement that same interface but for these different 00:35:26.333-->00:35:29.750 operating systems and, theoretically, it should compile. We're using cmake. 00:35:29.750-->00:35:34.000 We're using nothing Windows specific except for in the Windows scanner target, so if we 00:35:34.000-->00:35:38.750 add targets for OSS and Linux- OSX and Linux, everything should work so I want to do that. On 00:35:38.750-->00:35:43.833 top of that, I want to add targets for all major emulator types and for memory dump files 00:35:43.833-->00:35:47.750 because like I said before, we can scan anything that is bytes. Why limit ourselves to 00:35:47.750-->00:35:53.167 processes? I also want to add symbolic execution. So what some games will do is they'll store 00:35:53.167-->00:35:59.333 your health in a variable and that variable will be XOR'ed against another value in another 00:35:59.333-->00:36:04.750 variable somewhere in global memory or in the heap, right? And what that looks like is if 00:36:04.750-->00:36:10.542 you try to scan for your health, you try to scan for 500, 450, 550, you're not going to find it 00:36:10.542-->00:36:15.542 because it's encrypted with just a single integer XOR cypher. And it doesn't work. Right? And 00:36:15.542-->00:36:19.250 that's pretty common because XOR is so fast. You can do that and mess with the noobs who don't 00:36:19.250-->00:36:25.333 know what they're doing. So what I want to do is be able to go through the assembly and find 00:36:25.333-->00:36:30.042 patterns like that where we have a XOR followed by a XOR of another value, make the scanner 00:36:30.042-->00:36:34.333 take that into account, and when it does the scanning, XOR the two together instead of just 00:36:34.333-->00:36:38.417 saying "this address has this value," it'll say "these two addresses, when this XOR 00:36:38.417-->00:36:42.708 operation is applied, has the value you want." So I have some prototypes for that, but it's 00:36:42.708-->00:36:47.917 nothing great and it kind of breaks extensibility because I'd need disassembly for all the 00:36:47.917-->00:36:51.500 different architectures and stuff. Don't have that yet. I want to add it. And then I want 00:36:51.500-->00:36:56.000 to add native support for strings of fixed length which- within structures. Structures 00:36:56.000-->00:37:00.917 are only integers right now. Um, you can hack it together in Lua pretty easily using placeholders 00:37:00.917-->00:37:04.750 to just hold the place of the string pointer and verify it after the fact, but it's not 00:37:04.750-->00:37:09.750 there. Um, and, native support for pointers within strings. Stuff like that. So a lot of you 00:37:12.500-->00:37:18.333 here are like "Look, dude, this is DefCon. My company sent me here. Hacking games is fun, but 00:37:18.333-->00:37:23.375 I don't do it. I- I have work to do. How does this apply to me?" How can you use this? How can I 00:37:23.375-->00:37:28.542 use this is my normal day to day life? So if you're reverse engineering, I find this helps 00:37:28.542-->00:37:33.125 me a lot because I don't have to focus on figuring out what the relationships in the data are 00:37:33.125-->00:37:37.417 and what values are related. I can use the data structure detection to just tell me that 00:37:37.417-->00:37:41.750 and once I have that information it makes reversing it easier. Instead of going in Ida and 00:37:41.750-->00:37:45.583 saying "Okay, all these pointers are being followed. Kind of looks like a map. Now I have to 00:37:45.583-->00:37:50.625 somehow pull all the values out." Memory scanner does it for you. Helps. Um, and it's also 00:37:50.625-->00:37:56.000 great for just getting this hard to grasp glimpse of how the program is architected and 00:37:56.000-->00:38:01.083 designed which can be hard to get from assembly code sometimes. So just seeing how 00:38:01.083-->00:38:05.458 all the data's interconnected and where the values live can help out there. For offense, you 00:38:05.458-->00:38:10.500 can really just say "Screw the Lua" and just take the XenoScan engine library which stands 00:38:10.500-->00:38:14.000 alone and embed it in an application. If you're a bad guy or you're red teaming or 00:38:14.000-->00:38:18.583 whatever and you want to steal passwords out of a browser or a password manager or steal 00:38:18.583-->00:38:22.750 cookies or whatever, you don't have to write your own memory scanner. You embed this thing. 00:38:22.750-->00:38:27.125 You search for things that you know are going to be there or just search for data structures 00:38:27.125-->00:38:30.250 and then look at them and pull the values out instead of dumping all the memory and 00:38:30.250-->00:38:35.333 exfiling entire processes memory over the network which can be expensive. You can embed a 00:38:35.333-->00:38:39.833 memory scanner that's going to make that job much easier. Um, and it's great for research too. 00:38:39.833-->00:38:44.250 WHich kind of goes back to reverse engineering where it helps you focus on what the 00:38:44.250-->00:38:50.083 applications are doing aside from the data 'cause anything you reverse engineer. Anything 00:38:50.083-->00:38:54.708 you research. You have to focus a lot on how the data is connected. But if you could 00:38:54.708-->00:38:59.500 forget about that, you could focus on the algorithms that are changing and mutating that data. 00:38:59.500-->00:39:03.875 So I find it helpful there too. So the code's right here on GitHub. Completely open source. 00:39:03.875-->00:39:09.500 When I get good internet, I will go back to streaming on dev. I would love contributions from 00:39:09.500-->00:39:15.417 anyone who is probably a better C++ programmer than I am. Um, the slides will go up after the 00:39:15.417-->00:39:20.083 Con at XenoScan slides. Uh, you can probably just take down github.com/nickcano and find 00:39:20.083-->00:39:25.000 everything through there. Me, I'm at NickCano93 on Twitter. GitHub.com/NickCano. 00:39:25.000-->00:39:30.000 NoStarch.com/GameHacking is where you can find my book or you can pick it up at the No 00:39:33.708-->00:39:38.708 Starch booth 'cause I will be signing after this talk. Thank you so much DefCon. You've been 00:39:42.375-->00:39:43.542 awesome. [applause]