 Hey guys, welcome back skits on series episode 3 here bit of a bonus video I was falling behind on the videos get into forehead on the the coding side So trying to catch up with this bonus video the topic today is going to be mostly just command line arguments how that works in assembly It's not very straightforward At least it's not very cross-platform. I'll say that Then we're going to take a look at making some of these utilities from Unix like RM touch and CH mod from a scratch in assembly We're going to actually use those from now on in the series. So we're making our own tools here. Also, it's kind of crazy This is our third video. We haven't even covered how to print stuff out yet Yeah, we're already implementing utilities that we're going to use for the rest of the series. So kind of crazy We're gonna be doing some code golf Not the fake code golf that they do on like the internet where they just take stripping languages with like random letters meaning different things and calling that code golf I mean, we're gonna actually minimize the number of bytes being executed by a computer That's real real like IRL code golf And lastly, we're gonna disassemble our executables kind of can we can see like what's going on Like how did the a set the assembly organism translate our English into zero than one? So that's pretty cool. I I thought at least so in the system 5 a bi I covered this in the previous video very briefly but The initial process stack is very straightforward and very clear when you pass in arguments into a function Even if we get to main in C, this is how the stack is supposed to look you're supposed to have In RSP so I should say the register sorry the leprechaun whose name is RSP He's flipping switches and he's flip switches such that that number inside him that he's controlling is I don't know 50 obviously it can't be 50 has to be multiple of like 8 or 16 or whatever But let's say it's 50 just for a simple math here at address 50. That's the stack and In that at that first location there should be a number which indicates the number of arguments that you've passed in So let's say you pass. Let's see your command was echo high you your hacker your type didn't echo High and so that's two arguments one is echo one is high and the first argument is echo second is high and The argument count is too so at RSP there would be the value of 2 and then there'd be echo 8 bytes away And there would be high 8 bytes above that and then you'd have 0 8 bytes beyond that That's kind of how it works very simple The problem is is that just if I a bi it's supposed to control all these different OSes But and it does control Linux, but the guys on free BSD are on drugs. I don't know they made RDI Point to this not RSP and literally it's coin flip half the time Literally half the time not even when you install the OS like half time when you run software RSP points to argument count half the time it doesn't and that made me absolutely crazy I was trying to figure this out figure out why that was I couldn't figure it out Thankfully I posted to the free BSD for men to really smart guys figured it out for me again. Why on earth? They're allowed to violate a bi like that beats me All you have to know though is that if you're programming for Linux use RSP the point to RGC If you're programming for free BSD point to RDI because RSP only points there half the time for whatever reason And don't worry we're gonna implement this in such a way that no matter what you're compiling these things on if you get the Repository yourself it will automatically pull your OS and it will automatically include the right the right value for this We're gonna have like a variable to encode this register So that's cool Better example here, let's say you wanted to use LS You wanted to type on your console LS dash LA and LS by the way, you know, it's obviously it's a program It's a binary and here's where it's found on Linux at least on my machine And so this is how the stack looks before main is even called This is when the start label is finally, you know reached Obviously, there's two arguments. So at RSP in our previous example of address 50 And you know of leprechaun value 50 This is the value at Address 50 then you back to leprechaun. What's your number 50 add 8 to that? So just 58 in memory that would be address that there would be another number there That number happens to be another address at that address call it X There are three bytes The first one is the ASCII value for L then S and then a null byte That's what's called a null terminated string. It ends in a zero. That's what you do on C at least how you end the string That's how you get to how long the string is without including the number of letters ahead of time So it's you know, pretty cool to do it. And anyway, that's address X go back to the leprechaun What's your number 50 add 16 to that? That will be another number in memory at that address 66 At that go to go to that pointer and you'll have a this you'll have a negative sign and L and a and then a null These are all ASCII values. Here's the table if you're curious Go back to the leprechaun. What's your number 50 add 24? So address 74 in memory There will be a zero That means you're out of Out of arguments or you can just count this value is to this one's one this one's two you're done But anyway, if you didn't want to count you have a zero there just in case So that's how that works Kind of very straightforward only caveat is this is for Linux. You'd have to use RDI on free BSD That's how that works. So now let's go right ahead and implement some this is just a core functionality for these three Utilities so I use RM mostly to delete files. So just a single file here RM XYZ We're gonna replace that with recycle XYZ where millennials we have to recycle obviously that uses the unlinked syscall That's how you delete stuff on Unix. Then we're gonna implement touch X was that's how you make files We're gonna that's how I use it. That's how I use it at least I use it to make files I'm gonna replace that with spawn XYZ using the open syscall also the closed syscall And then lastly I use chmod predominantly to elevate files that are not executable to be executable So I do chmod plus X in fact, I've used that for every single binary. We've made in the series so far So I'm gonna replace this string with this Again, it's just gonna handle making things executable and making a single thing executable So not a full featured utility for sure, but definitely a minimal one So and that's gonna use the chmod syscall. That's you know the same name as the program itself. So Let's get into that right now Let me open up the Computer here. Okay Let me first show you all those Functions, so I got a couple functions here that you can use. They're all in the repository First one is file close, closes files, file delete, deletes files, open, opens files The last one is chmod that changes the permissions on a file. So how does this work? Well They're all kind of the same function with this with a little bit of difference here and there They all do this. They all save certain registers rcx and r11 and then they restore them afterwards in reverse order r11 rcx Why do they do that? It's because syscall Screws up those registers. Why does it do that beats me? Who cares? But yeah, you have to make sure that at least for us We're trying to preserve all registers in all functions. That's our our a bi that we're making here that everything is saved That's not a return value in our case. You can see here a return value is rex So I'm even though we're gonna change rex in the program I'm not gonna save and restore rex because I actually want to use it as an output in this case rex You can see here returns zero on a success and negative one on a fail and that's not my doing That's the syscalls doing that these OS Programmers already implemented that kind of return value in the syscall itself. So we don't have to worry about that Anyway, yeah, all we do is we save registers. We move the syscall ID into rex This will vary on Linux and VSD, but don't worry It will work on your computer if you're running it properly. It will pull the right value Then it will say the it will restore registers that we just saved and then return to the previous part of the code And that's how all this works. The only caveat would be on file open is that it doesn't return Success and fail it returns a file descriptor or a fail so in rex so when you when you work on like Unix you have to have a file descriptor to interact with the file. Well, you don't have to have it, but it's nice to have one So let's say you want to open a file and and or make a file called ransomware.exe right you want to email your friends this ransomware Anyway, in that in that file when you open it with this this command you're gonna pass in the file name a No terminated string here ransomware.exe Ending with a null byte. You're gonna pass in some flags and permissions and stuff And then you're gonna run this command and it will return this is called return a file descriptor Let's say 15 and then from now on whenever you want it right to that file or read from that file or interact with that file Anyway, you don't have to refer to it by its path File name dot exe you could refer to it by its number 15 and then when you're done actually You can close the file here You can see we close a file with a file descriptor so you can just whenever you're done with the ransomware file You just pass 15 into this function and it will close for you You don't have to have the path anymore just the number so it makes it a little bit easier You can now store a File in a register as opposed to having to store it somewhere in memory right a character Right you have to put in memory or as you can put a number in a register So that's pretty cool at least anyway, that's how the functions work for 10 minutes in here I want to show you the examples Did I show you something first? Okay before we get to the examples. I want to show you What's the motivation here? So I'm gonna show you two examples for each one one That's kind of just the bare minimum program and then like an actually code golfed version So we're not like one that the boomers will approve of that's very organized And when that's literally stripped down for bites as far as I can get it So you can see how this compares with Linux and free BSD Linux files Linux programs are massive compared to these DIY ones look at even Look at so we get 96,000 bytes versus a hundred and forty two bytes That's just a massive difference even RM come between free BSD Which is much better than Linux and our version. It's still a factor of 100 We are 100 times smaller than RM and granted We're not handling all the fancy flags and help and version stuff that the RM utility does We're just making it able to delete files But still it's pretty neat that we can do that in in one percent the amount of bytes as What free BST is using and free BSD is four times better than Linux. So that's just crazy to me, right? With that out of the way, let's get into the actual Programs here, so I have a couple of examples as I mentioned I have two for each one Let's take a look at the CH mod just kind of like the boomer version of this That's example a Let's take a look at just the code here and it's they're all kind of the same setup I'll go over just just one or two to show you how this works Actually, I'll go over all three just because it's it's very straightforward So we're including a couple things one is the syscall the things is all the numbers like That are different for different OSes So that's kind of how we're going to encode for both free BST on Linux Then we have our two syscalls We have one CH mod and one exit that these are functions that we've included that handle the syscall processing And here's the whole the whole code the first thing we do is We check for two arguments if you remember at address RSP So or RDI on Linux or free BST was supposed to encode where the What with the number of arguments was so let me go back to our thing So here at RSP that was supposed to have the number of arguments in it and we want it to be two We want to be able to have a second argument, which is a path We want to be able to change permissions or do whatever that requires a path in the second argument So we need this value to be two and then once we have the value of two We're going to access RSP plus 16 to actually get that value to get that multimated string So let me go back Here you can see we're checking if that byte at that address in memory on the stack is Two if it's not two as in you didn't pass a value or you passed way too many values You didn't pass a path or you passed way too many paths We're just going to jump to fail and that's going to set an error value of one and then leave the program If it was to as in you use the program correctly, it's going to not jump to fail It's going to do this. So what it's going to do is it's going to you Pull from RSP plus 16 or on free BST RDI plus 16 An address To a null terminated string a pointer to a string and put that in RDI then it's going to put the Execute permissions which we've defined in our in our other file. Don't worry about what that is Into RSI and we're calling CH mod. This is just inputs for the syscall I talked about in the previous video then all we do that at this point. We're done This made the function executable already this CH mod. We're done. The question is what's next now? We have to leave the program and so I'm making a return value of zero here I'm jumping to leave which is down here or exiting So now if you were to check the return value of the function It would be zero if it was a success it would be one if it was a failure. So that's how that works And let's just show you that it works Let's make a file. Let's use touch Brands somewhere And you can see let me look at the permissions It has just read permissions for the most part you can see and it's white. So it's not executable Now If I run the program and now have a binary, right? I can run that binary. That's our that's our function on ransomware Now if I look at this you can see ransomware is now read. It's now executable. So the function works Now let's go to our next example Example C this is going to be Touch so it's a little bit harder because it involves opening a file and then closing it If you want to be, you know, nice about stuff Let's open up the code ISM In this case as I mentioned before we have one more include we have a file open and file close But other than that, it's pretty much the same thing. But again, we have the same, you know comparison for two arguments Excuse me and Our first thing we do is we open the info file You can look at all of us how it works, but I'm very boring. We open the info file with certain flags and some permissions Then we so file open remember that returns the file descriptor in Rx So you can see here it returns that value in Rx. We move Rx to RDI. That's the input parameter for file close So now we can actually close the file that we just opened and this combination of things will have opened a file aka Created it. We've given it permissions or create file flags I hope this is a typo. Let's change that and Then we've closed that file. Now all we have to do is leave So again, it's the same logic to leave as before with that out of the way Let's let's run that and show you that it works. So We now have a binary so now we can do binary ransomware I can't spell And now you can see we have a ransomware file. The program works Let's now go to the last example that thing that was e Yep Let's open up the code. This is very easy. It's just removing a file in this case We have our file delete function I just talked about as well as the exit syscall and again, it's the same logic the only difference is in this case We're not checking, you know, we're not opening a file We're just deleting it straight up via the path. And so we're moving that No terminated string that address that pointer into RDI Calling file delete and then leaving so very simple nice and easy prove that it works. Let's make a ransomware file again You can see there it is and now we can create the binary there That is run binary on ransomware and you can see ransomware is gone. Nice and easy very cool Last thing I want to show you was this code golf version. Just saw the last one. Let's just keep it easy here example 3f This is a much smaller execute, but it does exactly the same thing. Let's open up code asm In this case, we have no includes why bother. This is this. This is our code golf attempt So we're trying to cut down bites. So why bother including Functions that we can just inline those functions, right? So we're not including anything anymore Now also why bother checking? I mean, we're trying to save bytes here. Let's not check for Proper inputs, right? We were checking for having two arguments. Let's screw that Let's say the user is smart enough to only pass in two arguments. Let's not even bother checking So we get rid of that part now. We're just leaving the file. We're moving that value directly from the stack that address To the RDI pointer and moving the syscall ID for delete into RAX and then calling syscall That's all it all this is and then we're exiting calling syscall. There's no error handling here There's no input processing here. Nothing like that. It's just the bare bones Well, last thing is that we can see we used the we didn't use the full RAX register We didn't use RAL. Why is that? It's because the number I think the value versus unlink on On on on FreeBSD is like 10 and I think on Linux. It's like 87 or something And so those values both fit into eight bits so we can just use the low low eight bits Register that's called AL and so that saves some bytes for us And we can do that both for the unlink syscall as well as the exit syscall I think that's one on FreeBSD and 60 on Linux again both those values can fit in eight bits So nice and easy. Now if I run that and Then I show you the file size you see this binary is like I said before only a hundred and thirty-two bytes Yeah, it works if I make ransomware and Then I show you that it exists and then I run the binary on ransomware It works. It leads the file and this is only with 12 bytes of actual instructions And that's what I want to talk about last is after we've talked about, you know, the going from assembly into You know binary like this Can you go backwards? Right? And the answer is absolutely. Yes And that's what we're doing here So for that DIY RM Utility here's what the binary looks like if you were to hex dump it without ending this as what this means Here's what you get this right column. That's just the ASCII stuff screw that ignore this whole column So the first 40 bytes hex or 64 bytes decimal that is the elf header Then the next 38 bytes hex or 56 bytes Decimal that's the program header those things are 120 bytes of just garbage that tells the OS how to open your program And what to do you can see 78 in helpful spots? That's indicating, you know, the address that you should actually start at in which case this is 71 2 3 Or sorry 71 2 3 4 5 6 7 8. Here's the start address, right? That's what that number is in coding Anyway, you only have 12 bytes of instructions and here they are all laid out And I've broken down what the op codes are to kind of get a feel of what's going on here So the easiest thing that we've got going on is we have two sys calls in the third position and in the last position and The code for sys call is just a straight up number. It's 0 f 0 5 and you can see those values Here and here That's nice and easy the next one is a little bit harder But not so much harder that is the instruction that we're using in the second and in the fourth slot That's moving an immediate value into an 8 bit or a 1 byte register in this case It was register al for both and what you do is you take b 0 hex and you add A number which Indicates the register you're talking about in our case register al the number for that is just zero So that's your first value b 0 that's what this comes from and this comes from that just means i'm talking about register al here And we're putting an al that's this ib that means an immediate byte And that byte is just sys unlink Or sys exit depending on which one you're talking about in our case for free bsd that value is 10 And if you look 0a is hexadecimal for 10 And then since exit on free bsd that was 1 and here you can see we've passed in the immediate byte of 1 0 1 Now what if you weren't talking about al what if you're talking about register bl So see al had a register kind of value of 0 But bl had a register value of 3 and so if you go back here to change this code from Move al to sys unlink to move bl sys unlink it would be b 3 0a Say that works because we just added 3 to b 0 You can do that for any of the registers that are in this table like that Obviously it gets a little harder with these so don't look at those but for the first eight you can do that Okay now The last one actually I should say the first one is the hardest That is the 64 bit version of the move instruction and we're moving a value from memory into a register And it's it's a little bit harder, but not so much harder. So the first of the easy parts, right? See this 8 b Well that straight up is right here. So that just encodes. Hey, I'm doing a 64 bit move instruction in this way 8 b leave that there. That's the second bite fine The next thing that's easy is this 10 that's hexadecimal for 16 if you look There's our 16 So that's where that comes from What about the other two bytes? What about 48 and 7f? Ha, this is some some tough stuff. So the first one is what's called the rex prefix This is all because this processor was like Not made for this purpose. This is like a 16 bit processor. That's been transformed to a Pretty big processor which then was extended to a 64 bit version. And so it's it's all over the place But anyway, how does rex? You know prefixing work. Well, I have that here So that's a bite the first four bits are always going to be zero one zero zero and the last four bits are Based off these four rows here in our case These are all zero the first one because we're using 64 operands. It's going to be a one So in our case the total number is zero one zero zero one zero zero. So zero x 48 That should be familiar because that is the first bite in our instruction That was the rex prefix Okay, lastly the 7f. This is cool. This is actually encoding Two different things. It's encoding this and encoding this And also it's encoding. It's getting us ready For this not encoding it but getting us ready for that. And how does that work? Actually, it's super cool That is what's called the mod rm encoding. And so here's how this works. Look at the table. That's pretty much how how it's set up So we're using rdi for both on free bsd. Let me go back So on free bsd. Remember this sys argc start pointer. That was just a variable that I made up to refer to rdi on free bsd and rsp on linux. So in on free bsd both the Destination which is this one and the source operand are both rdi. Keep that in mind Also keep in mind that the number that indicates rdi is 1 1 1 So the way this works is Also keep in mind Also keep in mind that this placement that we just talked about was 16 16 fits in eight bits Everything between negative 128 and positive 127 fits in eight bits. So we are one bite So we're going to be in this row for which the mod value is zero one So for this mod value, we have but we have zero one Then the two registers are the same. They're going to be rdi for both. Remember that was for both one one one or seven You just talked about that. So our total Op code or sorry, I guess bite here is this zero one one one one one one or break that down. It's zero x seven f That should be familiar because that is this So even these hard instructions They can be broken down they get a little bit harder You know the more and more stuff you end up doing But you can always break it down and figure out what every instruction is talking about Even without using some kind of disassembling software You can do this with just, you know The the documentation So it's very very easy Last thing I want to mention just a very pro tip here because it had me going for a loop for a while um, this is Let's say you didn't have eight, you know eight bits You had we're sort of having one bite you have four bites this placement Well, those bites are not going to be in the order you expect with the low bite on the far right The low bite will be first Which maybe you would expect that maybe you wouldn't I didn't expect that and so You know if you pass in the value that can't fit into an eight bit Displacement, let's say you pass in 128. It's not going to fit That that value that eight zero value for 128 that's going to be in The first slot of the displacement not the fourth slot of this placement If that makes sense great, if not I would who cares to just try to help the one guy That's going to help so anyway with that out of the way. I'm pretty much done with the video Not a very long one today. Um, we now know Mailing arguments command line arguments We also know how to implement some basic utilities using syscalls like rm touch and ch mod And we're going to use those for the rest of this series You know in my code If you look at the run.sh I'm using ch mod plus x every single time from now on We're going to be using our own version of that. Don't worry and then lastly We talked about code golf and also how to decode Instructions based off their binary numbers into what they're actually doing on the processor. So you guys You know are on your own now you can make your own utilities like like these three I did today Last thing I want to mention is I'm lonely and I have these Kind of slideshows from MS Paint, you know, and I want to share them with you guys So I'm going to make a A fed honeypot. I mean, sorry a discord server I'll put a link in the description. You guys can check it out and we can we can hang out there I can give you out these uh these note sheets. So Anyway, thanks for watching. Have a nice day