Stealthy Shellcode Execution: Parsing PE Resources Without WinAPI

Listen to this Post

Featured Image

Introduction

Embedding and executing shellcode in a Portable Executable (PE) file is a common technique in offensive security, but traditional methods relying on WinAPI functions like `FindResource` and `LoadResource` can trigger detection mechanisms. This article explores a stealthier approach: manually parsing PE headers and resource directories to extract and execute shellcode without WinAPI calls.

Learning Objectives

  • Understand how to navigate PE file structures to locate embedded resources.
  • Learn how to extract shellcode stored in the `.rsrc` section without WinAPI.
  • Implement a 4-step shellcode execution flow (allocate, copy, protect, execute) while evading EDR.

1. PE Header Parsing Basics

Command/C++ Snippet

PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)moduleBase; 
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE)moduleBase + dosHeader->e_lfanew); 

Step-by-Step Guide

  1. Locate DOS Header: The PE file starts with a DOS_HEADER. The `e_lfanew` field points to the NT_HEADERS.
  2. Access NT Headers: Contains critical metadata like the OPTIONAL_HEADER, which includes the `DataDirectory` array.
  3. Resource Directory: The 3rd entry in `DataDirectory` (IMAGE_DIRECTORY_ENTRY_RESOURCE) holds the RVA (Relative Virtual Address) of the resource section.

2. Traversing the Resource Directory

Command/C++ Snippet

PIMAGE_RESOURCE_DIRECTORY rootDir = (PIMAGE_RESOURCE_DIRECTORY)(moduleBase + resourceDir->VirtualAddress); 

Step-by-Step Guide

  1. Locate Root Directory: Use the RVA from `DataDirectory` to find the root resource directory.
  2. Iterate Entries: Resource entries are organized in a tree. Check `NumberOfNamedEntries` and `NumberOfIdEntries` to loop through resources.
  3. Find RCDATA: Identify your shellcode blob by its ID (e.g., 101).

3. Extracting Shellcode from RCDATA

Command/C++ Snippet

PIMAGE_RESOURCE_DATA_ENTRY dataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)(dirEntry + 1); 
BYTE shellcode = (BYTE)moduleBase + dataEntry->OffsetToData; 

Step-by-Step Guide

  1. Access Data Entry: After locating the correct directory entry, the `OffsetToData` field points to the raw shellcode.
  2. Calculate Address: Add the module base address to `OffsetToData` to get a pointer to the shellcode.

4. Executing Shellcode (EDR-Evasive)

Command/C++ Snippet

LPVOID execMem = VirtualAlloc(NULL, shellcodeSize, MEM_COMMIT, PAGE_READWRITE); 
memcpy(execMem, shellcode, shellcodeSize); 
VirtualProtect(execMem, shellcodeSize, PAGE_EXECUTE_READ, &oldProtect); 
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)execMem, NULL, 0, NULL); 

Step-by-Step Guide

  1. Allocate Memory: Use `VirtualAlloc` with `PAGE_READWRITE` to avoid suspicious `PAGE_EXECUTE` flags.
  2. Copy Shellcode: Write the shellcode to the allocated memory.

3. Adjust Permissions: Switch to `PAGE_EXECUTE_READ` using `VirtualProtect`.

  1. Execute: Trigger execution via CreateThread, QueueUserAPC, or other techniques.

5. Debugging and Validation

Command/C++ Snippet

printf("Shellcode base: 0x%p\n", shellcode); 

Step-by-Step Guide

  1. Verify Addresses: Print pointers to confirm correct shellcode extraction.
  2. Check Permissions: Use tools like Process Hacker to inspect memory regions.

What Undercode Say

  • Key Takeaway 1: Manual PE parsing bypasses API hooks, reducing detection risk.
  • Key Takeaway 2: Combining this with indirect execution (e.g., process injection) further evades EDR.

This technique exemplifies the cat-and-mouse game in cybersecurity: as defenders monitor APIs, attackers shift to lower-level methods. Future advancements in EDR may focus on heuristic analysis of PE structure manipulation, making obfuscation and dynamic loading even more critical.

Note: Always use these techniques ethically and in authorized environments.

IT/Security Reporter URL:

Reported By: Mikegropp Storing – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

Join Our Cyber World:

💬 Whatsapp | 💬 Telegram