Reputation: 31
Edit: My original question is no longer accurate, so I rewrote it.
So I'm writing a program to demonstrate a heap spray in C (which I know is unusual). I'm allocating a huge buffer (100MB) using malloc, but it allocates into a segment marked mapped rather than [heap] and I can't figure out why.
My code:
int NOPSize = 960;
int ShellcodeSize = 64;
int PayloadSize = NOPSize + ShellcodeSize;
char Payload[PayloadSize];
char* HeapBuf = (char*)malloc(0x6400000); //104,857,600 (100MB)
for (int x = 0; x < PayloadSize; x++)
Payload[x] = (x <= NOPSize) ? '\x90' : '\x41';
for (int x = 0; x < 0x6400000; x++)
HeapBuf[x] = Payload[x % PayloadSize];
printf("%x\n", HeapBuf);
free(HeapBuf);
Output of vmmap using GDB-PEDA taken from a breakpoint set at the call to free
:
0x80000000 0x80001000 r-xp /root/Documents/a.out
0x80001000 0x80002000 r--p /root/Documents/a.out
0x80002000 0x80003000 rw-p /root/Documents/a.out
0x80003000 0x80024000 rw-p [heap]
0xb19fd000 0xb7dfe000 rw-p mapped
0xb7dfe000 0xb7faf000 r-xp /lib/i386-linux-gnu/libc-2.24.so
0xb7faf000 0xb7fb1000 r--p /lib/i386-linux-gnu/libc-2.24.so
0xb7fb1000 0xb7fb2000 rw-p /lib/i386-linux-gnu/libc-2.24.so
0xb7fb2000 0xb7fb5000 rw-p mapped
0xb7fd4000 0xb7fd7000 rw-p mapped
0xb7fd7000 0xb7fd9000 r--p [vvar]
0xb7fd9000 0xb7fdb000 r-xp [vdso]
0xb7fdb000 0xb7ffd000 r-xp /lib/i386-linux-gnu/ld-2.24.so
0xb7ffe000 0xb7fff000 r--p /lib/i386-linux-gnu/ld-2.24.so
0xb7fff000 0xb8000000 rw-p /lib/i386-linux-gnu/ld-2.24.so
0xbffdf000 0xc0000000 rw-p [stack]
printf
outputs 0xb19fd008
which is in the top mapped segment.
Interestingly enough, the [heap]
segment disappears if I remove the call to printf
. So my questions, why is the presence of the [heap]
segment dependent on the call to printf
and why is the printed address in the mapped segment and not in the [heap]
segment? Thanks.
Upvotes: 0
Views: 344
Reputation: 140445
There's two things going on here, and before I get into them, I have to warn you that based on the presence of the string /lib/i386-linux-gnu/libc-2.24.so
in your map output, I am deducing that the operating system you're running your program on uses the Linux kernel and the GNU C Library ('glibc'). Some of what I'm about to tell you is very particular to those components.
First: for historical reasons, there are two different ways to allocate memory on operating systems that qualify as "Unix" (to first order: everything anyone still uses, except Windows): the system calls sbrk
and mmap
. sbrk
is much more limited; it can only manage the size of a single region of memory at a predetermined location, whereas mmap
(and its counterpart munmap
) can allocate and deallocate independent blocks of memory anywhere in the address space. Linux's /proc/PID/maps
file uses the [heap]
label for the memory region managed by sbrk
. Independent blocks of memory allocated with mmap
are labeled "mapped".
Second: glibc's implementation of malloc
uses sbrk
for small allocations and mmap
for large allocations (see the discussion of M_MMAP_THRESHOLD
in the mallopt
manpage). The default threshold between "small" and "large" is 128 kilobytes; you allocated 0x6400000 bytes = 100 megabytes, which is, obviously, much larger. The line of your mappings dump reading
0xb19fd000 0xb7dfe000 rw-p mapped
is exactly one page larger than what you asked for - the discrepancy is due to malloc
taking some extra space for its own bookkeeping information.
The [heap]
segment appears when you call printf
because - again, this is a quirk of glibc - the very first time you do anything with stdout
, some memory gets allocated internally, using malloc
. That allocation is smaller than 128kB so it gets put in the sbrk
region and there you are.
(Depending on what kind of "heap spray" attack you are trying to simulate, either you don't care that your 100MB wound up in its own mmap
allocation because you're just trying to cover enough of the overall address space that a wild pointer has a decent chance of pointing in there, or you need to allocate a large number of small objects, each the same size as some key piece of data in the vulnerable application, so that you have a decent chance of getting your allocations mixed up with the application's.)
Upvotes: 1
Reputation: 39298
I cannot duplicate OP's results. However, I'm running on an x86-64 kernel and 64-bit binaries, unlike OP.
Here is a small test program, test.c:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void self_maps(void)
{
FILE *in;
int c;
in = fopen("/proc/self/maps", "r");
if (!in)
return;
/* Let the C library handle the buffering. */
while ((c = getc(in)) != EOF)
putchar(c);
fclose(in);
}
int main(void)
{
size_t NOPSize = 960;
size_t ShellcodeSize = 64;
size_t PayloadSize = NOPSize + ShellcodeSize;
size_t BufferSize = 0x6400000;
size_t i;
char Payload[PayloadSize];
char *HeapBuf;
HeapBuf = malloc(BufferSize);
if (!HeapBuf) {
fprintf(stderr, "Out of memory.\n");
return EXIT_FAILURE;
}
printf("Payload = %p\n", (void *)Payload);
printf("HeapBuf = %p\n", (void *)HeapBuf);
for (i = 0; i < NOPSize; i++)
Payload[i] = '\x90';
for (i = NOPSize; i < PayloadSize; i++)
Payload[i] = '\x41';
for (i = 0; i < BufferSize; i++)
HeapBuf[i] = Payload[i % PayloadSize];
printf("\n");
self_maps();
free(HeapBuf);
return EXIT_SUCCESS;
}
If I compile and run it using
gcc -Wall -O2 test.c -o test
./test
I get output
Payload = 0x7fff3122b6b0
HeapBuf = 0x7f378806a010
00400000-00401000 r-xp 00000000 08:07 4587700 /.../test
00600000-00601000 r--p 00000000 08:07 4587700 /.../test
00601000-00602000 rw-p 00001000 08:07 4587700 /.../test
022c3000-022e5000 rw-p 00000000 00:00 0 [heap]
7f378806a000-7f378e46b000 rw-p 00000000 00:00 0
7f378e46b000-7f378e62a000 r-xp 00000000 08:05 1966262 /lib/x86_64-linux-gnu/libc-2.23.so
7f378e62a000-7f378e82a000 ---p 001bf000 08:05 1966262 /lib/x86_64-linux-gnu/libc-2.23.so
7f378e82a000-7f378e82e000 r--p 001bf000 08:05 1966262 /lib/x86_64-linux-gnu/libc-2.23.so
7f378e82e000-7f378e830000 rw-p 001c3000 08:05 1966262 /lib/x86_64-linux-gnu/libc-2.23.so
7f378e830000-7f378e834000 rw-p 00000000 00:00 0
7f378e834000-7f378e85a000 r-xp 00000000 08:05 1966245 /lib/x86_64-linux-gnu/ld-2.23.so
7f378ea2c000-7f378ea2f000 rw-p 00000000 00:00 0
7f378ea57000-7f378ea59000 rw-p 00000000 00:00 0
7f378ea59000-7f378ea5a000 r--p 00025000 08:05 1966245 /lib/x86_64-linux-gnu/ld-2.23.so
7f378ea5a000-7f378ea5b000 rw-p 00026000 08:05 1966245 /lib/x86_64-linux-gnu/ld-2.23.so
7f378ea5b000-7f378ea5c000 rw-p 00000000 00:00 0
7fff3120c000-7fff3122d000 rw-p 00000000 00:00 0 [stack]
7fff3126c000-7fff3126e000 r--p 00000000 00:00 0 [vvar]
7fff3126e000-7fff31270000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Because the version of the GNU C library I'm using uses memory mapping for allocations larger than 130 kB or so, HeapBuf
is in a mapping of its own:
7f378806a000-7f378e46b000 rw-p 00000000 00:00 0
Upvotes: 1
Reputation: 44828
HeapBuf
resides on stack just like any other int
s, long
s, char
s you have in your code because the pointer itself is merely a number. But the memory it points to is indeed allocated on the heap.
Upvotes: 2