Reverse Engineering Anti-Debugging Techniques (with Nathan Baggs!)

John Hammond · Beginner ·🔐 Cybersecurity ·1y ago

Key Takeaways

The video discusses reverse engineering anti-debugging techniques with Nathan Baggs, covering topics such as game preservation, low-level programming, and cybersecurity, with a focus on analyzing and bypassing anti-debugging methods in a game using tools like X64 debug, Gedra, and Capstone.

Full Transcript

Hi everyone. Yeah, this is awesome. Super good to be hanging out with uh Nathan Bags. Finally get a chance for us to chat. And look, for folks that aren't too familiar, Nathan has a phenomenal YouTube channel where he's been sharing a ton of low-level stuff, a little bit of security, a little bit of like game hacking, a little bit of mods, a lot of reverse engineering, and I'm excited to finally get a chance to chat, learn from you a little bit, and I think you've got some showand tell. But before we dive into the fun stuff, Nathan, I don't know, can you do your spiel? Who are you? What are you up to? How's the channel going? Tell me everything. Of course. Yeah. Yeah. So, yeah, as you've already alluded to, my name is Nathan Bags. Um, I've been a professional software engineer for probably about a decade, maybe a little bit longer across several industries, including cyber security at one point, but mainly like low-level C++ is what I've kind of really sort of been doing for most of my professional life. Yeah. So, I I have opinions on the language, both good and bad, but it's kind of my bread and butter. But, I've been fortunate enough to do some like reverse engineering and some low-level stuff within that kind of sphere as well. And yeah, um I started YouTube about 3 years ago. At that point, I'd been writing my own game engine for about 2 years and I thought, oh, I'll do some YouTube videos on this and we'll see we'll see what happens. So, my first few videos were on that and it had like okay success, like you know, thousands of views, which is more than I ever expected when I um when I started. So, and then I sort of I had one big hit, which is still my biggest video, which got like 800,000 views. It's Yeah, it's absolutely mad on like comparing C++ and assembly. Yeah. And it's definitely not my style of video now. and I look back at it like there's a lot I could have done better but like I'm still still proud of of it for what it was at the time but I could never really recreate that success and I was just sort of started trying different things like I didn't want to just be doing C++ over and over again totally out the blue I did a video I thought I'm just going to sit down I'm going to do some reverse engineering cuz I've done it before and it's a skill that I want to kind of get back into a little bit keep sharp because I don't do it professionally and I sort of sat down with Roller Coaster Tycoon because as as legend has it it was written by one guy entirely in x86 assembly so I thought well let's let's pull this apart let's have a look let's try and figure out how like I don't know. I think the goal was like how does some of the rendering work? So I sat down with X64 debug and Gedra and I sort of recorded like a couple of hours of that and I think I cut it down to like 40 minutes and it was just like it was what it was and I threw it out there and I think it got like a thousand views. I was like okay that's that's not it. Let's try something else. And then I mean as you well know like YouTube is a is a strange and mysterious beast because almost exactly 30 days later uh it it exploded. it got like 100,000 views in like over like the next like week and I was like, "Oh, maybe this is it." Um, so from there I started doing more reverse engineering. I looked at some games from my childhood like Worms 2 and some stuff like that. And then I just started yeah just evolving into that. Then I started looking at DRM like how does old school DRM work? And now what I'm kind of looking at now is uh I guess more like game preservation. So there's a lot of games out there that don't work on modern Windows and that's an absolute tragedy cuz these are these are experiences core parts of people's childhood and fun. So, I'm now sort of looking a little bit more into that, like what does it take to get these games that don't work to work? And that's like looking at the differences over different versions of Windows, like doing some patching, doing reverse engineering, sort of like debugging on kind of hard mode. It's like a production build with no symbols, highly optimized, and it doesn't bore like it it crashes here. Like, can it not? So, that's that's kind of what I've been looking at at the moment. That's kind of where I'm now. That is super cool. And seriously, genuinely, I'm trying to, hey, help folks go take a look at Nathan's channel because it has so much cool stuff in there and it's just fascinating. How long have you been doing I mean, I know you said YouTube, 3 years, 10 years for some software development. When did it kind of oh, click in to think let's pivot to either that game reverse engineering or that game preservation. Was that relatively recent? When was that amongst year or so? I'd say maybe a year and a half. I think that's probably when it started doing and I was like, "Okay, well, let me try to do some more videos on this." and then they took off and yeah it's yeah so probably about that sort of time frame and definitely I think going to be the foreseeable future it's a lot of hard work but ultimately rewarding like I've had people message me saying hey this game now works and it's my childhood and it didn't work before so thank you and that ultimately like what a great use of uh my my my time that is I think that is probably very cool feeling that that is awesome you take any I don't know any requests do you have stuff on the horizon oh the next things you're tackling what a great segue into what I want to show you today but as a small plug I do have a Discord now which I'm slowly growing and there's a channel in there for video suggestions and I get stuff sent stuff all the time like I have a very open email address. I have open DMs. I'm happy for me to contact me about anything and I get lots of suggestions of like this doesn't work or can you look at this and I I look at them all and I make a note and that list is now pretty pretty long and every now and then I sort of triage it and I go through and I'm like ah what's what could be interesting because somewhat selfishly it does need to serve a video I think like you know to kind of grow the channel and kind of grow the sort of the reach of it all. So, if I think it has that sweet spot of being technically interesting, achievable by just me in my free time, like I have a full-time job that's not this and a family. So, and I think makes a compelling video, then if that sort of trifecta lines up, then, you know, we'll give it a go. Someone did post in there about a game from like a point-and-click adventure game from their childhood, Disc Noir, which I've never played, but it's based on uh the novels by Terry Patchet, who is one of my favorite authors. So, I was like, "Oh, well, the interest is there." They said that it crashes on Windows 11. Like the cut scene, you get into the cut scene, it crashes. Like, okay, interesting. This was released in like the late '9s. It might not have even supported XP. So, there's probably a difference in Windows since since then and now. So, I was like, okay, that's probably it. And whenever I start off, I do like a little bit of elaboration. So, I sort of head down just hack on it just to sort of scope out the problem like is this viable? Is this doable by me? What do I think the end result is? And that elaboration for this video has gone on for 3 weeks now. And as you'll see, I'm actually no further towards my end goal than I started. So like if you're watching this and you suggest that video, like I still weasel words. I cannot like there's no guarantee I'll actually be able to do it. But I'll show you what I've done so far today. Uh cuz it's kind of interesting. Been a lot of battle to I'm almost at the point where I can start doing the fixing if that uh and you'll see what I mean by that hopefully as we get through it. Totally. Well, do you think we should dive into screen share right away button and see what happens? Uh right. I've done my best to invigen all the fonts because I have I work off a 42-inch monitor. Um, so I can have everything on screen at once so I can fully absorb all the misery. Um, so this is my usual workflow is I'll have a VM or a debugger on the left and then I'll have Gedra on the right and then offscreen on another mon notes and which is where my notes are for this video and our faces. And yeah, I just sort of flick between the two. So hopefully that's kind of visible. I guess you know zoom in and cut in as you need to. I'm just going to start talking and chip in if there you know if you've got any questions basically. So, I don't own I've never played this game. I don't own a copy of it. So, I found a copy of it on some dodgy abandonware site, which I seem to spend an awful lot of my time on, which is what hence why it's in a VM, cuz I absolutely don't trust any of that stuff on my host machine. I will trust a CD that I've brought off eBay for some reason, but uh cuz it look it looks legit. But yeah, definitely a random ISO from from the internet. I'm uh maintain a healthy level of skepticism. So, here's the ISO that I've mounted. Um, and there's a few interesting things in here that immediately like jump out to me as as I look at this. Like, there's these random files called like 1.temp temp and there's a ICD file and a clo.exe and these are all indicative of safe disc. So sort of very very classic '90s early 2000s DRM that you was commercial you bought it off the shelf you slapped it in your game and then you hoped that people couldn't break it. Safe disc has been broken for many many years and there are no CD cracks and breaks for these online. Part of what I want to try and show people is like the techniques that you use to solve these problems not just go to this site and download this random DL and slap it in your directory and hope for the best. So, I kind of don't look too much into the prior art. I try and figure it out as I go along. Like, as if we were back on the scene in the '90s, like, you know, I'm nowhere near the level of technical ability that those guys were who were like a new game would come out and they'd have it cracked, you know, be a race who could crack it first and they'd probably have it cracked within like, you know, a week or days. I'm in my own time kind of recreating those glory days. So, this is going to be that kind of DRM. There's an interesting thing with this DRM. This Well, there's two interesting things immediately here is there's the SEC drive. This is a Windows driver that the game installs for you, which is so lovely of it to install uh a Windows driver for you. The thing with this DRM is I don't know the full technical details of it, but basically it embeds special information in the disc such that commercial CD copiers won't be able to duplicate it. It has like bad sectors and like uh like incorrect CRC checks. So if you try and clone it, you will not get a 100% clone of the disc. In that bit that they've missed, they'll put important information that verifies you had the disc. So this game for even though it's mounted as an ISO, this game will know that it's not a real disc. And the other thing is that this driver has a vulnerability in it and Windows discontinued it. Uh so after a Vista, you cannot play any game with this DRM because this driver will not run. Oh wow. So that also an issue. So those are kind of the things that you know are kind of up against. I've done videos on this before. So this is going to be kind of going off some previous dives on this. Yeah. So that's that's what that file does there. This ICD file, this is version one of Safe Disc because this is the game, but it's encrypted. Oh. So what it does is it installs a launcher and the launcher runs verifies you've got the CD binds the encryption key off those special sections of the CD that weren't copied over then it decrypts the game and runs it for you. So from this ISO alone would it not be able to run? No, we I'll show it in a minute. But um so this is like a PE file. It's an executable file in itself. But in the sections um this code section here is all it's all gibberish. If I it's all garbage. So if I go 64,000. So if I just these should be the bytes, but these these these are non sorry the font size is a little bit smaller. I can't control this. But these are like nonvalid instructions, right? I don't I don't know what more than the STSD7 is. It's unlikely a compiler is probably meant to is is intended to put that there. So yeah, you if you try and launch this, it just won't it won't work. You need like the pre-step which does effectively the diff the disk authorization for you. Interesting. So that's kind of the first thing. So I've installed it here. So this is what it looks like when it's installed apart from these two files which I've added which I'll get on to later cuz that's part of like the bypass. But yeah, basically you run tin 3 DXD and then this will go it will look for your disc in this case the E drive. It will find the ICD file and it will try and decrypt it. Obviously it won't be able to because it doesn't have all those precursor steps. And in fact because this is Windows 10 it won't even get that far. So if you just try and run it like I think this is because it's trying to access a driver that is unsigned and Windows is disabled. But even if you just like I don't know hope for the best and run it as admin. I'll run anything once in a VM. Um like nothing nothing happens, right? So this is about as far as we get. So the first thing I would normally do now is like right let's let's fire this up in the debugger. Let's start poking around the guts. Let's you know I've already got it kind of open in Gerra over here so we can kind of look around a bit. Let's open this up. So forward and if I run it immediately it's like ah what what you doing? Oh that that's the game that has like the anti-debugging techniques. So, so we already know we're on to like an interesting journey here because the game itself is trying to prevent us doing this. So, so it's like, right, okay, so I want to see why the game doesn't work. I first need to run the game, but in order to figure out why I can't run the game, I now need to attach to a debugger. So, I guess we're going to look at the anti-debugging techniques first. So, this is kind of like I'm already two steps back from where I want to be starting. So, this is kind of like okay. Um, I think if you run that again, first thing that I like to do is look for strings. Pretty pretty standard uh practice. So if I search for strings, unload the debugger, the string is there. So the string is not offiscated, but it's in like the Windows resource section. So there's no there's no references to this. Like I don't know what code is loading it because it's going to go through the win32 API for loading like the internal XC references. A lot of what I do is like what can I actually be bothered to do? Like do you know what I mean? Like it's trying to where where's my time best focused? What's the easiest probably to do is just set a break point on the function that produces the message box cuz that's definitely hit and then presumably somewhere before that it's like a if there's the check. Yeah. If if something show the message box. So what we'll do is we'll just do that. So symbols I know it's in our user 32. LL and then it's uh message box A. So if I just set breakpoint on that and then we run through to it. So we're here now. So we've hit hit the message box. So it's nice and simple. If we go up the call stack, we can see it's called from here. Yeah. So it's called from here. So then we can go over to Gedra and have a look at where we get to here. I think this is some sort of localization stuff like the get system default lid. I again I take a look at this and like whatever this does is somewhat irrelevant because at the end it calls message box. So that's presumably like you know this this function is producing a message box. Let me go up the call stack to where that's called. So this is then I'll start trying to just figure out where things called from. So it's called from here and this is this is interesting right? So it's like if this variable doesn't equal zero then call this function which shows a message box. And I've already started adding some words to this. So this is not that's that symbol wasn't in there. So you can assume any function names I've added through this sort of journey. So just for me to regurgitate if that's okay. Yeah of course. Yeah. Yeah. Uh hey you opened it up. try to do some dynamic analysis first with the debugger debugger wind at us because hey, you shouldn't be using a debugger. So you're thinking, all right, that's fine. Let's let's table dynamic analysis. Let's go static analysis with Gedra. We can look for the string of that message box. Well, that string is not an easy place. So let's kind of zoom out. Let's just look for the get message box or show message box function call. Got to do that in the debugger because it was just before you'd hit that trap. But then we can say, okay, let's look at the call stack. Let's see what actually brought us to that place and let's just take that address, go look again in Gedra. Kind of peacemeal back and forth in both static and dynamic analysis. Just taking one address to go look at the next spot in the next spot. Cool. Correct. Yeah. So, I think in this one I've attempted to patch it out before cuz I've just zored it out. So, let's let's take ignore that for a second. Let's go to uh I've got another I have many hedras open. Let's go to a cleaner one. Um so, this is this is this function, right? that this is the one that the result of this says, "Oh, you should show the message box or not." Now, I don't know about you, but like this doesn't look like code. Like it it looks like an absolute hot mess. Uh and the assembly is also very strange. So, let's put a little break point on this. So, again, lots of red angry from the debugger like it doesn't like disassembling this code. So what you do is if you start stepping through it instruction by instruction does the function epilog and then it goes jump and it goes jump and it goes jump and it goes jump and it jumps and jumps and jumps and jumps and jumps and jumps and jumps and jumps and basically they put in 50 100 150 jump instructions in amongst all the code to make it so that all this tooling doesn't really work. And it's even worse than that because here I don't know if it wasn't that obvious uh some of the instructions are unaligned as well. So you can't even see where the jump instructions and this one calls STC which changes the flags in the CPU. So then like your relative jumps might do something different cuz it now thinks like a number is less than another number or whatever. So this is pretty grim. And then it like at this point like equals get version XA which doesn't seem that important to me. So maybe there's extra code in there that looks valid that isn't. And then it does another jump and then you keep going through. So yeah, basically what it does is it does loads of jumps and then it'll do like a little bit of code and then it'll do some more jumps and then it'll do a little bit of code and then it'll do some more jumps and then a little bit of code and this when you try and decompile that. So you try to huristically turn that assembly back into C code is just like reams and reams of rubbish. Like nothing in here makes much sense cuz all the valid code is interspersed with it. So like I mean what's your thoughts on that? Like how would how how would you approach this problem? Because I'd be kind of curious to know given like there's very little information what your initial thoughts are on this. I feel like that's a neat technique. I feel like I would let myself probably fall down the rabbit hole a little bit more parsing through step instructions to get to where's basically the last thing that would be just before it returns the value yes or no pop the message oh you're in a debugger bail out cuz if we could just knop that or patch it to whatever semblance of saying like okay no you're fine don't worry I know I'm in a debugger but let me keep rolling that's really the sweet spot that I'd be looking for even if it's in this jumpy, spiky, random code assortment. Is that kind of right? I I think there's no right or wrong in this. There's just different levels of misery. Uh I think I think that's that's basically it. But like this jump I don't know if there's a name for it. This jump chaining thing is all over the binary. It's literally almost every function has this in. So it's already starting to slow us down. Can I fix this? So I thought, how would I have written this in the first place? Because no programmer is sitting there handrolling 50, 100, 150 jump instructions, misalign instructions. Like it's clearly tooling done, right? It's like a post-processing step. Like your developers will write to the game and then they'll buy in the software and then you'll presumably throw it at that and then it will do it. There's probably like an algorithm that it does. And if I was doing it, like the simplest way, most the jumps just tend to be forward. So what I would probably do is I'd probably break up like a function into blocks of assembly, maybe even ones that represent a line of code if you had like the PDB file or something like that. and then I would spread them out and then I would shove those jumps in between them. That's how I would do it. I would just like expand the code and fill it with with like jump chains. So what I did is I started writing some code uh to basically heristically detect these jump chains. Uh so it's a little built-in disassembler. I don't have you heard of a library called Capstone? I have. Yes. So this just wraps Capstone. Uh it's just a free pretty good decompiler. So you give it some C super cool. Well, I wrap it in C++. So uh cuz so here's my C++ abstraction around around the C API. But yeah, so you give it some bytes and then it will attempt to disassemble it and give you what looks like this. So the pneumonics and the arguments. So I chew through that and then basically the code's not it's not my best code. This probably wouldn't be on my CV, but basically I starting at the beginning of the binary and moving one bite at a time, I try and disassemble every single block of bite, you know, in a sliding window. Uh, and if it does disassemble and the pneumonic starts with a J, I'm like, that's probably that's probably a jump instruction, right? Cuz I don't I'm not an x86 expert. I don't think there's many pneumonics that start with a J that aren't a jump. So, I'm like, okay, fine. And then what I do is if I if I like that's a jump, I'll take the argument, which is the offset, and then I'll jump to that point and I'll disassemble it. And if that starts with a J, I'll take the offset and I'll disassemble it. And I effectively effectively simulate the code through these jump chains. And I keep track of all the ones that I've done. And then I have to handle like a whole load of miserable edge cases like that STC thing we saw. I have to handle that because that might affect which way the jumps go and whether they're taken or not. There's an interesting one where if you have an if statement and that starts the jump chain, I don't want to include that first if because then I might accidentally over overcommit and take out like the else branch or the other thing. So I have to then kind of be like, oh, if it's the first one, I've got if it's the first one and it's a J or J ne cuz heristically those seem to be the only ones that do this account for that blah blah blah blah blah. And then I'm like, okay, I will um I'll log this as like what I've called like a jump chain. So like the beginning I'll work out where this the start of that jump chain is and where the instruction is that it ends cuz I know after that is code and then I'm like if the chain is I don't know I just took a guess like more than three cuz I don't want to be too overzealous. Then I will go um then I'll knock it out. So I will then replace all those jumps with knots myself. And that's kind of my my attempt to like deobiscate this code. And that's uh where's where's a good example of this? So here is where was that function we were looking at? So this function here becomes this. So this is the same code but I've knocked out all the jumps. And now Gedra is like ah I can skip past that and now it gives me much more readable code. So it's gone from this to this. Much nicer a lot easier. And it is not perfect. I've made some mistakes. Sometimes it gives up halfway through a long chain. I don't know. I've not finished debugging it all. And I'm not sure if I ever will. But it got me enough to get going. And that's kind of part of the game, right? is to build enough tooling that you can proceed without thinking I have to productionize this and make it pretty and make it perfect. So it's not perfect but now I can see like okay so it calls get version XA based on the platform ID which it should be two it does you know it calls this function so we can call check into this function that calls this function which would be on the unclean one would be uh this so so all this is just this one call and then I was reasonably pleased with myself and I started looking through all this and it was just like it just keeps going and going and going I not I'm not really getting any further here like I definitely will need this clean code later, but like this one debugger check is not worth that time at the moment because what I what I ended up doing was is I just found the call site and I just patch I just changed the return value of that function in the debugger to basically say whatever that check was it was fine so we could at least get to like the next one if that makes sense. Spoiler spoiler alert there's another one. Great. Um so I'm just going to wire that up. If you've got any questions whilst I just quickly sort of set that up then let me know. There's quite a lot of information very quick there. I mean, so I obviously I feel like in stuff that I've got to play with previously, whether it's like, oh, doing some reverse engineering, capture the flag challenge or like binary exploitation pone task. Like I've played with capstone very cursory cursory, not not a lot in Python, but I'm like, "Hey, Nathan, I know you like big fan of C++ and like it's hardcore. You got some awesome syntax to still be using Capstone, but I'm thinking, oh dude, I've just done that in easy baby Python cuz I'm scared of C." Uh, is there a real particular advantage for you one way or another? Do you feel like it makes a big difference or not? If you're like, hey, I still just I know that I'm cutting up this binary and I'm not worried about it being production code. I'm not worried about it being fast. I just need it to be complete and done and working. No, you should pick whatever gets you to the result fastest. If if it's not like I mean, I've worked as a professional software engineer. I've like had to ship code and then like maintain it. It's like a product and then as like internal tooling. like you should do whatever gets you there fastest. I absolutely like because like time is a reasonably precious commodity, right? So I just it's just because I've done C++ for so much I I know it's probably the best out of any language and that doesn't necessarily mean it's better or worse than anything else. It's just what I'm kind of familiar enough with to kind of get going. Uh if that makes sense. No, totally. Yeah. So here here's here's here's the test, right? So I'm going to do is I'm going to put a break point on this test. Uh and then I'm going to move forward here. Okay. So this is the test. So this has returned one. Okay. whatever that check was which again like I kind of took the view that at this point in time maybe maybe I will need to go back and look at it but for now I know that it is returning a value based on whether there's a debugger attached or not for now because I might be able to patch the binary later like you saw you know I can patch out or whatever just returns zero and then that allows me to continue and then we hit here so this is this is interesting this is again in a massive jump chain in fact it's underlined because if I scroll up the debug's lost it so it's already like on a weird unaligned instruction oh yeah let's go you're hitting an interrupt right there exactly Yeah. Yeah. Yeah. So, this is this is cute. I like this. Int one is what debuggers use to tell the kernel that they want to single step. So, when I'm pressing F8 to single step an instruction, it's sending that interrupt to the process. So, it cuts back. They have deliberately put that instruction in there. So, when running under a debugger, the debugger thinks it's debugging it and it will stop there. Oh, yes. That is clever. That is easy. It is because what's more interesting than that is that there's the concept of structured exception handlers in Windows. So in even in C you can have try except in Windows. What they've done is and I'll show this. Let me see if I can find it. Uh this is how I figured this out. I started looking at the beginning of all this misery and there's oh my god there's just so much somewhere in this mo fs oxo like segment register is how Windows code compiles the beginning of a try catch block. So they've wrapped all this code in try catch. Okay. And what int one would normally do when not run under a debugger is trigger the catch block. Okay. But when running under if I just step over this in a debugger, it won't trigger the catch block. It will just step over normally. So that catch block is probably having some sort of side effect that naively if you were just hitting this in the debugger and stepping forward, you would never execute. And we can we can see this, right? So this the way this works is like in the assembly is you push this function and this is the accept clause. So when when an exception is triggered, it will call this function which is this. And it's doing some unwinding stuff. It kind of looks exceptiony, but what we can do is we can set a breakpoint on this. We can tell the debugger to pretend it's basically not caught it and continue as if the program was executing normally in this case, which is quite handy. In x 64 debug, if you do debug advanced run pass exceptions, it's now stopped at the exception handler. So I'm now in the except as if the co and this is exactly what the code would be doing normally. And it's not not offiscated, which is nice. They obviously weren't expecting anyone to kind of land in here. And then like there's this weird like I don't know what this is, but it's it's calling some function that it's resolved from somewhere, right? Yeah. So I was like that seems seems suspicious because a lot of these have names like global unwind and early rect but somewhere in here. I think it's got to be what this is that the call uh where where the call's in here somewhere, right? Uh there okay here. What's that doing? Why have they resolve some random function? So it's like step down into that. Uh, and if we step into that, we end up here again back into Gedra. It's why it's nice having them side by side. This just calls another function which is massively offiscated. Uh, but if we take this offiscated one and look it up in our clean one, it's just setting a single value. It's setting a global flag. All that offiscation is doing is hiding setting a global flag. And I'm like, okay, well, I kind of want to know what this is. So, again, now that we know what this instruction is, cuz this is, you know, we just knocked out the code, but the original code is still there at the same addresses. So we can go over here. We can set a break point on this and then we can run through it. Stop there. It's like edx is like co5. That seems like a very deliberate number. Do you know what I mean? Like I don't know what it is, but it's it's it's not it's not an address, you know? It's not like one or two. It's very much like it's very much a magic number something. Yeah. So a trick I like to do is when I'm trying to pass through and you might be able to do this directly in Gedra. I don't I don't know like uh but I like to dump all the disassembled C code into a text file so I can search it. So if I I've got Notepad++ here and this is all the disassembly for every single function all in one massive long file. So everything we've seen so far from the unstripped from the non-clean version. This is all the code or all the attempted code. So if you just search for like I know C O five is that right? I was like, "Ah, okay. It's doing doing something here." And then it's checking this 8 this 42C800 is not this magic value. So starting to put the pieces together now. There's there's a special exception which which it will try and not get you to if you're running under a debugger. And then that will set a flag and then later on it will set that flag. Oh, now I need to find this in the code. Scroll up all because again this is not the cleaned one. I just did this like one of the first things I did. I should probably do this for the clean one. I get the function name and then if we go to the let's do the dump first. The it's in here somewhere. I don't know if I can even bother to scroll down and find it. Um who knows it's in here somewhere. But if you do it if you do it in the clean version again sort of the power of building out your own tools. It resets the value. It calls that in one function which is what I've renamed it to that and then immediately afterwards it checks it. So all that is do all that is doing is just set resetting the flag triggering the interrupt and then checking if the exception handle was called. Okay, that's a much smaller of a process now that we've made sense of it. Yeah, exactly. Yeah. Um, so yeah, that's um that's what uh that's doing. Um if you continue though, so the problem I had here is that uh it does this a couple of times and then crashes, right? It just hits like a bad like a bad instruction. So like something is going on. If you look at the uh memory map of this, so where is this actually in the in the in the binary? uh it's in the text section and then the text section here is like lots of like gibberish. So again this this binary itself has obfiscated code within it. So before it gets to decryptting your game it needs to kind of decrypt or deoffiscate itself first. Presumably in that is the is the decryption code. Yeah. So we're again like another step back. So my kind of intuition I guess or guess is that there's another anti-debug check somewhere a silent one which we're triggering and it's causing this to not happen because it's going to it's gone through it's not de offiscated it but it's still told it to go and try and execute the code as if it has which will inevitably crash it. So what's the move here? Do we kind of walk backwards via call stack again or so what I what I do here is I then go I then at this point I'm like okay I I need some need some guidance. If you go to this website, this is basically a collection of pretty much all the known anti-debugging techniques in Windows. So if you look at say for instance uh exceptions in here will be the the int one thing that we saw somewhere. Maybe it's not this one, but that's in here. There's um a whole bunch of others. So really it's just a case of grinding through these and starting to check them. So I'll pull out a couple that I found so far, but spoiler alert, I I took this opportunity to move on to like another angle. But let's go through a couple of the other ones are interesting. So some of the classic ones are debugger flags. So these are like special flags that get set in the binary by by Windows when it's under a debugger. So like is debugger present is a function that you can call to say am I under a debugger? It's pretty much the most simplest naughty one. Um but let's go kernel 32. See if they even use that. Yeah, exactly. Yeah. So let's start again. We'll go forward. We'll go remember to reset our because we not patched this out yet. reset our initial check which doesn't use that. So that initial check is still doing something unknown and then it hits it. Okay, fine. So we could patch this. We could hook it. There's loads of things we could do. This is this site also tells you how to bypass these as well. So it's it's pretty trivial to do. I started looking at some others. Another one I like to look for is uh by oxcc x86 instruction which triggers a breakpoint. That is how debugs actually work under the hood. Every time I click this red dot, it silently patches that binary with oxcc. but just doesn't show you. So when it runs, it hits that and then the kernel looks to see if you've got a debugger attached and calls into it. So what this code here does is it's scanning its own memory to see if you've put any break points in. Wow. Yeah. So uh where where's this do this live because this is a fun one. I like this one. I've seen this before a couple other games. Not one of these is like foolproof, but if you put enough of them in and sort of sprinkle them around, it does start to get a little bit irritating. That's so funny to me because I'm I guess I'm like used to maybe my small brain like in a malware sense. There's normally like one anti-debug trick or two, but I would not expect like seven or eight. And this is like 20 years ago. Like if you look at something like Denuv now, which like I am nowhere near smart enough to look at that is like layers upon layers upon layers of complexity. Where is the check? I just want to see if I can see oxCC in here because this is it's kind of again it's not a little cute one to actually see happen because I think my clean one has overzealously stripped this out. So again I've kind of lost the context here. I sort of have to gauge roughly like how far I'm away from the bottom. Uh very tiny vertical scroll bar. Yeah I mean you can probably search within Gedra. I think it's quite important to say that I'm not like a master any of these tools. I've just picked up enough to be dangerous and like I could definitely be a lot better at a lot of what I do. Here you go. Here it is. So, here's the check. So, if we put this in here and we go here and then set break point here. If we run through to this, it's now stopped. And what's it looking at? It's looking at ECX. Let's step through again. Oh, no. Actually, what I want to do is I want to put it on the where it actually passes where it thinks it's found it because this is just it checking everything. So, if it equals here, I want it on this ad. Here you go. Look, you can see here that it's detected we put a break point on his debugger present because it's going to it goes through the kernel 32 imports. It basically says, are there any kernel 32 functions that you've been trying to that you've been trying to debug? If you've been naughty and trying to trying to do that what you're not supposed to be doing because it this will then detect it. That's neat. Yeah, it's fun, isn't it? And one last one super quick is if you search for uh FS offset, this is that special segment register. This gives you this is how you can get special information about your process from the that the colonel has created for you. So when you launch a program in Windows, it kernel automatically creates you a thread execution block and a process execution block. These are strrus with like specific details about your process that exists one per process. And then there's a special like assembly instruction you can do to get get the pointer to that basically. So for instance like here this is interesting to me because it's got a bunch of random offsets and if you grind through this there is a special flag in one of those strokes which is is a debugger present. So this is manually checking like the kernel structures that are created to see if there's a debugger present and that's all listed in the site as well. That's another fun one. If you got time for one more thing before I move on thing. Absolutely. Where where let's go back a sec. Let's go back to because this is this is the bit I was like I can fix all these. I can patch the binary. These are all solvable problems. I'll show you the bit that broke me for now. Uh uh and we'll see where we get to here. So I was like okay. So I started p I started patching these out in the binary. you saw that like zor ex to return zero for that and I was like it wasn't working which was a little bit upsetting. So what I did was is I was like I took it right back to like square one cuz I was I'm pretty sure I found them all and it still wasn't deviscating the code. I was like is it is it the patching? Can it detect it's been patched because that would be annoying right then what you going to do I don't know what to do. So here this this line here resets that global the int 8 exception set to oxfff right. So and then later on it sets it to co5. So I was like okay let's just I changed this to e right so it's semantically still the same like it's still going to be a invalid flag and then it will try and set it to the valid one. So it's like a single bite change and it didn't work and that was the only change I made. I was like oh now that's interesting. So what I then did was is I then followed in dump I set a hardware breakpoint on it. So hardware break in Intel in x86 there are four special hardware breakpoint registers that you can put an address into and the CPU will send uh a message to the kernel saying someone has accessed this and it's much less detectable than a software breakpoint. It's a it's what's called a hardware breakpoint but what's interesting about it is you can say please only break when someone reads it or writes it or executes it. So a normal breakpoint will only stop when you execute it but this is like at this memory address when any code writes to it please stop or when you read from it. So I made this one bite patch and uh it still broke. So I was like something is something might be reading this. So you do break point hardware access like on this bite and continue and then oh I need to remove that. Continue shift F9. Okay rep mo SD is uh basically mem copy. Uh so something is reading this address. This is broken because something is reading this address. And then you put this into over here. And then I've started reversing this and basically it turns out that it's it's reading its own code into a buffer and then doing something with it presumably doing some sort of sanity check against it. Uh, and then it's got like this weird like flag which controls whether it does it through like a mem copy or whether it uses like a wind32 function. And then you kind of start stepping through all this and like trying to name stuff and like uh sometimes it reads a file, sometimes it does all this, sometimes it it does some function resolution. And basically I came to the conclusion that it's taking its own code and producing maybe some sort of check sum and using that as part of the key for deoffiscating the code because every time I change that bite the deoffiscated code was different. So I was like, "Right, okay." So now I'm another step back. Is that So can I parallel that to like sort of shadow stack uh like a canary of look, this is the code how it should have been originally and if it was tampered with at all, we'll bail out. So that feels very shadow stack to me. Am I am I right? Yeah. Yeah. I think it's basically the same principle. It's that it it can tell that you've modified it, which is what you need to do to bypass all the all the anti-debug things. So, so before I can before I can find out what's wrong with the game, before I can run the game, before I can debug the game, I now need to patch the game, but before I can patch the game, I now need to figure out what what the check summon decryption like defiscation is for that. And I think I I've got a reasonably good idea how to bypass this. I just like I'm like two weeks in at this point and I the the starting line is getting further and further in the distance. Um, so I thought, okay, let's take another crack at this. let's just see if we can get it running because we there's we've still got the safe disc driver to contend with and that's more of a known problem and I've done that before. So I was like let's just see if we can get it running outside of a debugger at least at least if we get the game running we might be able to attach a debugger to the game cuz this is still not the game right this is still the launcher. So I was like let's take a let's take a take a break here. So the way safe disc works is it loads a loads a driver and I believe that driver has the low-level like knowledge to to read from the CD disc, right? Because it's a it's a kernel driver so it has much more lower level access than just the user land. That's my assumption. I never really looked at it. Basically what it does is it makes a load of calls to the driver through something called device IO control. It's a win32. It's like I Linux. It's a way of just interfacing with the with a device, right? So the driver exposes a device IO control endpoint and the game connects to that and what it does is it sends a challenge and expects a response basically in a lot of cases. So what I and this is not uh a novel idea. People have done this before but I did it myself because I wanted to see how it worked is I implemented a shim so that it I hooked all the device IO control functions so it talked to me thinking it's the driver then I could give it back the correct answer and I just reverse engineered out the correct answers. Nice. Yeah. So that's in code again not not my not my not my greatest code. um that looks a lot like this. I also use this for as uh a generic a general sort of hooking thing for the for this project. So these are all the win32 functions that I've hooked and then I've just got code in here which allows me to basically shim them all. So so I think here like uh look is debugger present I can hook that and just say no. So stuff like that. So that's how you can get brown back because this is undetectable because um this all uses IAT hooking. So when an executable loads a DLL, it basically creates an array of function pointers saying once you've loaded the DL, can you please put the function pointers in this like array and I'll know the offset. I know is the debugger present is the 10th entry for instance. Because it won't know what address that DL will be loaded at until it's loaded. So I just overwrite that with a pointed to my function. Cool. And you can still call the original, but then you can save the code. So if you look down, if I go uh again, ignore my uh it's pretty cool. Yeah, I have this hook IAT function which does the monkey work of going through the PE file, finding like where the IAT is resolving it and then stamping it in and then it takes the hooked function and then the original function. So when it finds the IAT entry, it pulls out what was in there, saves it off and then shoves in the new function. So I'll be like in kernel 32, there's create file A, please replace it with this function and stick the original function here. And then I've just done this like adnauseium with everything that I need to do. Nice. That's still so cool. It's pretty good. Like Yeah. And this is like again this is not novel. People have known how to do this since Windows, but it's and there's frameworks you can do for it, but I like to do it from scratch because I think it's kind of it's interesting to know how these things work under the hood. And I think it makes for slightly better storytelling as well. Yeah. I would be like, "Oh, let me use min hook because that's again all my small brain knows." But it's still But again, like if you needed to do this, then you should absolutely just do that. I'm I'm a bit of a gluten for punishment and I like to kind of figure out how these things work under the hood. But like again, if that's not your end goal, then you should just use whatever's off the shelf and just to kind of get you going. So here we do device IO control and it has a special number that is this is this is the special CRM number. So if it sees this, then intercepts it. If it sees anything else, it just forwards it on to the to the original because you don't know what else it's going to be doing. And then it's basically got a buffer and inside that has like a control code and then it just expects different responses uh on this basically. One fun thing is that cuz what's interesting is I've seen this driver before in other games and it's always normal but in this one they've also put that job offiscation on it as well. Uh which I've not seen before. So that was kind of fun. So I cleaned it again. I used my code to kind of clean it up a bit and I started uh naming things. Uh precisop. So I've named all these things and somewhere in here it is it builds this like special verification number into it like almost like a check sum which uses the kernel tick time. So the number of ticks since the machine was booted and it does some does some maths on that to produce like a check sum and then it sends it back over and then the game presumably verifies that. So I had to emulate that. I don't have kernel tick time in New Zealand. That's that's purely a kernel thing. Turns out though, if you just read from this address in any Windows process, you'll get the kernel tick count. It's just the Windows maps in for you into every process. It's just at that address. Okay. So, I did that. That's a fun fact. Yeah. It's like the K user data structure or something like that. There's a whole bunch of stuff. Windows always maps in a

Original Description

Check Nathans channel out here: https://www.youtube.com/@nathanbaggs His video on this game: https://www.youtube.com/watch?v=PKlOCMjaGdc Learn Cybersecurity and more with Just Hacking Training: https://jh.live/training See what else I'm up to with: https://jh.live/newsletter ℹ️ Affiliates: Learn how to code with CodeCrafters: https://jh.live/codecrafters Host your own VPN with OpenVPN: https://jh.live/openvpn Get Blue Team Training and SOC Analyst Certifications with CyberDefenders: https://jh.live/cyberdefense
Watch on YouTube ↗ (saves to browser)
Sign in to unlock AI tutor explanation · ⚡30

Playlist

Uploads from John Hammond · John Hammond · 0 of 60

← Previous Next →
1 Code Commentaries? PHP to JavaScript in Bash and PHP!
Code Commentaries? PHP to JavaScript in Bash and PHP!
John Hammond
2 Tutorials? MySQL connection with PHP and Bash!
Tutorials? MySQL connection with PHP and Bash!
John Hammond
3 Variable Naming in Python! Happy Birthday, Linux! Nokia N900!
Variable Naming in Python! Happy Birthday, Linux! Nokia N900!
John Hammond
4 JavaScript Splits The URL!
JavaScript Splits The URL!
John Hammond
5 HTML Tables in Python!
HTML Tables in Python!
John Hammond
6 HTML, Net Shares, GML!
HTML, Net Shares, GML!
John Hammond
7 Python 08 Programming Style and Comments
Python 08 Programming Style and Comments
John Hammond
8 Python 26 Object Oriented Programming
Python 26 Object Oriented Programming
John Hammond
9 75 Python Tutorials, Out Now!
75 Python Tutorials, Out Now!
John Hammond
10 Batch 14 Mathematical Expressions
Batch 14 Mathematical Expressions
John Hammond
11 Batch 85 Array Append
Batch 85 Array Append
John Hammond
12 Batch 86 Array Count
Batch 86 Array Count
John Hammond
13 Batch 87 Array Index
Batch 87 Array Index
John Hammond
14 Batch 88 Array Insert
Batch 88 Array Insert
John Hammond
15 Batch 89 Array Remove
Batch 89 Array Remove
John Hammond
16 Batch 90 Array Reverse
Batch 90 Array Reverse
John Hammond
17 Python [colorama] 00 Installing on Linux
Python [colorama] 00 Installing on Linux
John Hammond
18 Python [colorama] 09 Cursor Position
Python [colorama] 09 Cursor Position
John Hammond
19 Python [hashlib] 02 Algorithms
Python [hashlib] 02 Algorithms
John Hammond
20 Python 00 Installing IDLE on Linux
Python 00 Installing IDLE on Linux
John Hammond
21 Python [pygame] 11 Rectangular Collision Detection
Python [pygame] 11 Rectangular Collision Detection
John Hammond
22 Python [pygame] 12 Platforming Rectangular Collision Resolution
Python [pygame] 12 Platforming Rectangular Collision Resolution
John Hammond
23 Python [XML-RPC] 01 Research
Python [XML-RPC] 01 Research
John Hammond
24 Python [pyenchant] 03 Personal Word Lists
Python [pyenchant] 03 Personal Word Lists
John Hammond
25 FancyURLopener Authentication and User-Agent [urllib] 03
FancyURLopener Authentication and User-Agent [urllib] 03
John Hammond
26 Python 04: PEP8 Coding
Python 04: PEP8 Coding
John Hammond
27 Python Challenge! 17 COOKIES
Python Challenge! 17 COOKIES
John Hammond
28 Google CTF 2016: Ernst Echidna
Google CTF 2016: Ernst Echidna
John Hammond
29 Google CTF 2016: Spotted Quoll
Google CTF 2016: Spotted Quoll
John Hammond
30 Google CTF 2016: Can you Repo It?
Google CTF 2016: Can you Repo It?
John Hammond
31 Google CTF 2016: No Big Deal
Google CTF 2016: No Big Deal
John Hammond
32 Google CTF 2016: In Recorded Conversation
Google CTF 2016: In Recorded Conversation
John Hammond
33 Homemade CTF Challenge: 01 "Orchestra"
Homemade CTF Challenge: 01 "Orchestra"
John Hammond
34 Homemade CTF Challenge: 02 "Bae's Base"
Homemade CTF Challenge: 02 "Bae's Base"
John Hammond
35 Homemade CTF Challenge: 03 "Web Hunt"
Homemade CTF Challenge: 03 "Web Hunt"
John Hammond
36 Homemade CTF Challenge: 04 "UPX"
Homemade CTF Challenge: 04 "UPX"
John Hammond
37 Homemade CTF Challenge: 05 "The Assumption Song"
Homemade CTF Challenge: 05 "The Assumption Song"
John Hammond
38 Homemade CTF Challenge: 06 "A Brisk Stroll"
Homemade CTF Challenge: 06 "A Brisk Stroll"
John Hammond
39 Homemade CTF Challenge: 06 "I lost my password!"
Homemade CTF Challenge: 06 "I lost my password!"
John Hammond
40 web25 :: Mr. Robot : EKOPARTY CTF 2016
web25 :: Mr. Robot : EKOPARTY CTF 2016
John Hammond
41 web50 : RFC 7230 :: EKOPARTY CTF 2016
web50 : RFC 7230 :: EKOPARTY CTF 2016
John Hammond
42 misc50 : Hidden inside EKO :: EKOPARTY CTF 2016
misc50 : Hidden inside EKO :: EKOPARTY CTF 2016
John Hammond
43 Hack The Vote 2016 CTF: Sander's Fan Club [web100]
Hack The Vote 2016 CTF: Sander's Fan Club [web100]
John Hammond
44 Hack The Vote 2016 CTF Warpspeed [forensics150]
Hack The Vote 2016 CTF Warpspeed [forensics150]
John Hammond
45 Juniors CTF 2016 :: Black Suprematic Square
Juniors CTF 2016 :: Black Suprematic Square
John Hammond
46 Juniors CTF 2016 :: Six Strange Tales
Juniors CTF 2016 :: Six Strange Tales
John Hammond
47 Juniors CTF 2016 :: Lost Code
Juniors CTF 2016 :: Lost Code
John Hammond
48 Juniors CTF 2016 :: Here Goes!
Juniors CTF 2016 :: Here Goes!
John Hammond
49 Juniors CTF 2016 :: Southern Cross
Juniors CTF 2016 :: Southern Cross
John Hammond
50 Juniors CTF 2016 :: Clone Attack
Juniors CTF 2016 :: Clone Attack
John Hammond
51 Juniors CTF 2016 :: Dirty Repo
Juniors CTF 2016 :: Dirty Repo
John Hammond
52 Juniors CTF 2016 :: Hackers Blog
Juniors CTF 2016 :: Hackers Blog
John Hammond
53 Juniors CTF 2016 :: Voting!!!
Juniors CTF 2016 :: Voting!!!
John Hammond
54 Juniors CTF 2016 :: The Good, The Bad and The Junkman
Juniors CTF 2016 :: The Good, The Bad and The Junkman
John Hammond
55 Juniors CTF 2016 :: Stop Thief!
Juniors CTF 2016 :: Stop Thief!
John Hammond
56 Juniors CTF 2016 :: ROFL
Juniors CTF 2016 :: ROFL
John Hammond
57 Juniors CTF 2016 :: Restriced Area
Juniors CTF 2016 :: Restriced Area
John Hammond
58 Juniors CTF 2016 :: Oh SSH!
Juniors CTF 2016 :: Oh SSH!
John Hammond
59 HackCon CTF 2017 TRIVIA and BONUS Challenges
HackCon CTF 2017 TRIVIA and BONUS Challenges
John Hammond
60 HackCon CTF 2017 "Bacche" Challenges
HackCon CTF 2017 "Bacche" Challenges
John Hammond

This video teaches viewers how to reverse engineer anti-debugging techniques used in a game, using tools like X64 debug, Gedra, and Capstone, and provides hands-on experience with analyzing and bypassing these methods.

Key Takeaways
  1. Set a breakpoint on a function
  2. Go up the call stack to see where the message box function is called from
  3. Use static analysis with Gedra to look for the string of the message box
  4. Use dynamic analysis with windbg to look for the get message box or show message box function call
  5. Patch the return value of a function in the debugger to bypass a check
  6. Identify interrupt used by debuggers for single stepping
💡 Anti-debugging techniques can be bypassed by analyzing and understanding the underlying code and using tools like debuggers and disassemblers to identify and manipulate the code.

Related Reads

Up next
Why Cyber security is important for your SEO
Menerva Digital
Watch →