 Hey everybody, welcome to please inject me, X64 code injection. So let's start a little bit about me. My name is Alon. Been to the cyber security industry for about seven years. Started at the IDF in the IntelliJet division and now working at Deep Instinct. At Deep Instinct I'm researching for new attack vectors, doing some heuristics, and then finding new ways to attack. Also reversing malware. So that's pretty much about me. Okay, so in this talk, first I'm going to give you a short introduction about code injection. So it's going to be really short. I'm going to talk about what is inject me, the new code injection technique I found. And then I'm going to give you a little bit of a technical background just to know things that you need to know before you can use the method to do the code injection. And at the end there's going to be a detail flow of how to do the code injection. Okay, so what is code injection? Code injection is the term of inserting code from one process to another and then execute it in the context of the other process. So it looks like this. First we inject code to the target process, message box, and then we execute the code. Okay, so through this presentation I'm going to use the term target process and injecting process. Okay? Amazing. Okay, so why code injection is important? Okay, code injection is important because it can be used for malicious and benign activities. Okay? So it can be used for malicious activities like for stealth, okay? If a malware offer wants to hide the presence of the malware and make it harder to find it, it can inject the code into another process and then it will be hard to detect it. It can be used for evasion so we can inject into a security solution, okay? And then we can bypass the security solution but if you inject into the solution they won't scan the activity of the malware. We can also if let's say one program, only one program is allowed to do something and we inject to that program we're going to be running in that context and then we can use the context to do something malicious and bypass the security solution and at the end we can steal information, let's say phone grabbing and browsers and then we can see credit cards and data and things like that. And for benign users we can use it to in security solutions, we can use it to get the activity of a malware, okay? And the malware we can see what it does and if it does something malicious we can just inform the user and tell them that something bad happened. Also it can be used to add some functionality to a program so we can add something new that is not written in the code. And at the end we can also do some monitoring so a security researcher can monitor the activity of a malware and then see what's going on, it's not only a malware, it can be for any program. Okay, so how it all started? I was researching a few methods of code injection to do mistakes and then I thought to myself why wouldn't I do my own code injection technique? Maybe I should look for it. So I thought a little bit about it and I had an idea, lucky for me it worked, the idea that I'm presenting today. So this new method is going to be used in 864, okay? The idea about behind it is we are going to use read process memory in the target process, it's going to execute the process memory and then read from the injecting process, the share code. This is why I'm going to use the term injection-ness because we really are not really injecting code into the target process, we make it read from us. Amazing. Okay, so now a little bit of technical background. Read process memory. A WinAPI function in Windows and this function what it does, it just reads data from a process, it gets five parameters, H process which is going to be the process we want to read from, LP base address which is the address we want to read from, okay? LP buffer which is the address we want to write the data we read to and N size is going to be the size we want to read and LP number of bytes is going to be the number of bytes that were actually read. Okay, now X64 calling convention. In X64 calling convention, the parameters are passed on the registers. So they are passed at RCX, RDX, R8 and R9. We can pass these four parameter arguments to a function and then when we execute it, the function is going to use those arguments. Arguments after the fourth parameter are passed on the stack. So if you see this diagram here, you can see that if we call read process memory, R6 is going to be the H process, RD is going to be the LP base address, R8 is going to be LP buffer, R9 is going to be N size and then LP number of bytes is going to be on the stack. So if we look at the stack, the return address is going to be at the top of the stack, then there are four arguments that are ignored, data there is not important and then at the end there is going to be the fifth parameter at the stack. So again, if we use separate context, the function and set those registers, we can just manipulate a function to execute with those parameters. Okay, so through this presentation, you're going to see this timeline. It's going to show us where we are. First, we're going to talk about how to set up read process memory. Then we're going to encounter the first problem which is access violation, we'll then encounter another problem that we will need to create an infinite running thread and at the end we're going to execute the whole method. Okay, so let's start. Read process memory. Okay, as I showed you earlier, it gets five arguments. Okay, and as I already told you also, we can only pass four arguments using the registers. Now lucky for us, the fifth parameter can be null, then it is ignored. And more lucky for us, if you use virtual alloc EX, the allocated memory is going to be all zeroed out. So we can create a dummy stack that is going to contain that fifth argument. So this is good for us. Okay, so let's start setting up read process memory. So the first parameter we need to set up is H process. Okay, H process we can set it up by using duplicate handle. So we're going to duplicate the handle of the injecting process into the target process. And then we have the handle inside the type process, we can use it as a process. Then we will allocate memory for our shell code. Okay, the dummy stack we already allocated earlier, the base address of the shell code we already know because it's in the injecting process. And the size of the shell code we already know because it's also something we know in the injecting process. Okay, so let's say now that we actually read the, called and executed read process memory. We're going to execute it and we're going to have our first problem which is access validation. So what's going to happen is we first call read process memory. The memory is read to the buffer and then we go to the return address. Now because the stack is all zeroed out, we're going to return to the address zero which is invalid. And we're going to get access validation. Not nice. But if we put as the return address a function like RTL exit user thread, we just exit the thread in a clean way. When we execute read process memory, it will be called memory is read to the buffer. We return to the address on the stack and the thread will execute in a clean way. Amazing. Okay, now we want RTL exit user thread to be on the return address in the stack. Okay, so to do this we have two steps. First we need to find a place where RTL exit user thread address exists. Okay, so let's first talk about kernel 32 and NTDLL. Okay, those are both DLLs in Windows that are loaded in each process at the same location. So the base address of them is similar in each and every process. And kernel 32 also imports the function RTL exit user thread from NTDLL. So what this means is if we look at the import address table and we find in our version, in the injecting process version, the address of RTL exit user thread in the IIT, it's going to be at the same place in the target process. So we can copy that there using a method I'm going to describe right now. Okay, so to copy the address we're going to use the function NTQU APC thread. Okay, this function gets a thread handle which want to execute our code in that thread. Then it gets the function we want to execute and we are allowed to pass three arguments. Okay, so we are going to use the function RTL copy memory. Okay, give it destination source and length. Then the target process is going to execute RT copy memory. Now the destination is going to be the return address of our dummy stack. The source is going to be the address of RTL exit user thread in our version of kernel 32 IIT. And the length is going to be 8 bytes which is the size of a pointer in x64. Okay, now a little side note, we can use this method of copying. Okay, we can use it to recreate a shell code. So we just need to find in our version of let's say NTDLL the bytes of our shell code one by one. And then we can just copy it in the target process and we can just recreate the shell code there. Okay, now let's finalize the execution. And so my first idea was to set RIP, um, to read process memory, set all the other registers and then just execute it. But the truth when you do so, um, you get, you do it on a suspended thread and then you resume it, you get the exception status invalid parameter. Um, and we don't want that because that makes the process crash. And even if the, if something happens it just will crash and won't work. Um, and if we, uh, want to do something with a thread, we want to do something, uh, to copy it to read process memory, uh, we need the thread to initialize before we can manipulate it. Okay, uh, another thing if we will start a thread, uh, and it won't be suspended, it will just run, terminate before we can manipulate it. And that will be bad for us because we won't have time to do our stuff. Um, so to do it we need to create, uh, infinitely running thread. Uh, we'll create that thread and then we'll manipulate it, um, to run, uh, read process memory. Okay, so when create remote thread is called, um, the things, the first function that's going to execute is RTL user thread start. Um, this function first called to, uh, a function. After that you just do other stuff and then does jump RBX which is going to be the entry point. Uh, you deliver to create remote thread or create thread or whatever. And I guess you haven't seen it but RBX was never, never, never used here. Never, RBX was never changed, um, during this time. So we are going to abuse that and we're going to use the opcode jump RBX, um, to do the infinitely running thread. So what we're going to do is we're going to allocate memory for jump RBX. Then we will look for jump RBX copode opcode in our NTDLL version in the injecting process. We will copy it using the method described earlier to the target process. Then we'll create a remote thread that is suspended and it's going to point to the jump RBX that we just copied. Um, then using set thread context we are going to set RBX to point to jump RBX and then resume the thread. So not, what's going to happen is going to execute, get to the jump RAX we talked about earlier. And then this is going to jump to the jump RBX. RBX points to the same location. So just going to jump over and over and over and over and over again. And that's how you make it run infinitely. Okay, so now let's, uh, execute the code. Let's just execute the code injection. Um, so now we will suspend the thread and see that the RIP really points to our jump RBX opcode so we know that the thread was initialized and everything is okay. Um, and then we'll start setting the thread. Okay, so RCX is going to point to the duplicated handle we duplicated. RDX is going to point to the address of the shellcode in the injecting process. The buffer, the LP buffer which is R8 going to point to the location we allocated for the shellcode. Um, R9 which is, um, the size is going to be, um, where we, um, size of the shellcode. Then RSP is going to point to the dummy stack we allocated. So the fee parameter will be null. Um, and RBX is going to point to read process memory. So when we are going to resume the thread, jump RBX is going to jump to read process memory. All the other parameters are already set up. Um, so we're going to do that. We'll call wait for single object. Um, just to see that the thread was finished. When it's done, the shellcode is already copied into the, um, target process. And now all we have to do is just use a function like create a mod thread and execute the shellcode. So now for demo. So in this demo, I'm going to open a notepad, execute the code injection method and you're going to see at the notepad the command line in, in a message box, of course. Um, the command line of the, uh, notepad that we opened. So here I opened the notepad. Please inject me. We start running the program. We find, duplicate the handle of our process to remote one. We allocated memory for shell code. Then we allocate memory for our dummy stack. We will find out the exit user and copy it to the dummy stack. We will allocate memory for jump RBX. We will copy jump RBX into the local memory. Find the process memory. Then we'll create a mod thread at point to jump RBX. We'll set the thread context. Context was changed. We will execute the process memory. And now execute the shellcode. And when you look at the notepad, you can see the message box with the command line. So thank you very much for having me. I hope you enjoyed the talk. Uh, special thanks for, uh, Madison for helping me doing it. Um, the white paper is going to be published in the next few days. It's still not out, but, uh, you're more welcome to ask any questions. Um, if I have time, I think I have. Um, thank you. Are there any questions? Okay. Okay. Thank you very much.