Windows Malware Development Part 3: Payload Placement Basics - .data

2 minute read

Objective

Hey guys, welcome to the third part of Windows Malware Development, in which we will be beginning with the basics of payload placement. In this post, we will be looking at how to place our shellcode in the .data section

.data Section

Placing our payload in the .data section is relatively easy. All we have to do is make sure that our shellcode variable is defined outside of our main() function.

You will need Visual Studio/Visual studio build tools, x64dbg and ProcessHacker for this exercise.

Writing the code

So, using our code from the previous post, all we have to do is remove the #pragma directive and the declspec line that tells it where to store the shellcode. Also, we do not define it as a const:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

unsigned char Text_RawData[] = {
    0x90,    // NOP
    0x90,    // NOP
    0xcc,    // INT3
    0xc3     // RET
};

int main() {
    printf("[i] Text_RawData function: 0x%p \n", Text_RawData);

	// Allocate executable memory.
    size_t codeSize = sizeof(Text_RawData);
    printf("[1] Press <Enter> To Allocate Memory ...");
	getchar();
	void* executableMemory = VirtualAlloc(NULL, codeSize, MEM_COMMIT |  MEM_RESERVE, PAGE_READWRITE);
	
	if (executableMemory == NULL) {
        printf("VirtualAlloc Failed With Error : %d \n", GetLastError());
        return -1;
    }
	
	printf("[i] Memory allocated at : 0x%p \n", executableMemory);
	
	// Copy the shellcode from Text_RawData to the executable memory.
    printf("[2] Press <Enter> To Write Shellcode ...");
	getchar();
	memcpy(executableMemory, Text_RawData, codeSize);
    
	// Change the memory protection to PAGE_EXECUTE_READ.
    DWORD oldProtect;
    if (!VirtualProtect(executableMemory, codeSize, PAGE_EXECUTE_READWRITE, &oldProtect)) {
        printf("VirtualProtect Failed With Error : %d \n", GetLastError());
        return -1;
    }
	
	// Execute our shellcode
    printf("[#] Press <Enter> To Run ...");
	getchar();
	((void (*)())executableMemory)();
	return 0;
	
	// Free the allocated memory.
    VirtualFree(executableMemory, 0, MEM_RELEASE);

    printf("[#] Press <Enter> To Quit ...");
    getchar();
    return 0;
}

Analyzing in x64dbg

In order to get a better understanding of how this works on a machine level, lets have a look at our program in x64dbg.

If you are using VS build tools, you can use the below command:

cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /Tcfilename.cpp /link /OUT:output.exe /SUBSYSTEM:CONSOLE /MACHINE:x64

Now, lets run our program and attach the debugger to it. As our console prints out the address of our raw data array (our shellcode), lets first go to that address by jumping to it:

jmp_data.png

Once we reach there in our CPU window, we can see the series of instructions of our shellcode. If we right click on the first instruction of our shellcode, and follow it in the memory map window, we can see that the shellcode resides in the .data section now:

data_sec.png

There are no changes in the execution part of our program, so execution will happen in the same way as the .text shellcode program from our previous post.

That’s all for this post! See you in the next, in which we will be covering the last part of payload placement, which will be using the .rsrc section.