x86_64

All entropies are too high to bruteforce. However, only the offset between shared libraries is fixed.

The entropies of heap addresses are relatively small ($13$bit, $8192$) than others. I suspect, this depends on the implementation of libc.

x86

The entropies of text or libc is $8$bit. It is hit with only $256$ trials (feasible). The stack has entropy of $11$bit ($2048$), and the heap is the same to the one of x86_64.

data

$ uname -a
Linux localhost 4.4.0-36-generic #55-Ubuntu SMP Thu Aug 11 18:01:55 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 5.4.1-2ubuntu1~16.04) 5.4.1 20160904
$ gcc a.c -o x64
$ ./a.py ./x64
text:	[0x400000, 0x400000] (0x0)
heap:	[0x604000, 0x2600000] (0x1ffc000)
libc:	[0x7efc24547000, 0x7ffbedc1d000] (0xffc96d6000)
ld:	[0x7efc24910000, 0x7ffbedfe6000] (0xffc96d6000)
stack:	[0x7ffc028e8000, 0x7ffffe1e5000] (0x3fb8fd000)
heap - text:	[0x204000, 0x2200000] (0x1ffc000)
libc - text:	[0x7efc24147000, 0x7ffbed81d000] (0xffc96d6000)
ld - text:	[0x7efc24510000, 0x7ffbedbe6000] (0xffc96d6000)
stack - text:	[0x7ffc024e8000, 0x7ffffdde5000] (0x3fb8fd000)
libc - heap:	[0x7efc231d8000, 0x7ffbec94c000] (0xffc9774000)
ld - heap:	[0x7efc235a1000, 0x7ffbecd15000] (0xffc9774000)
stack - heap:	[0x7ffc00c22000, 0x7ffffcd79000] (0x3fc157000)
ld - libc:	[0x3c9000, 0x3c9000] (0x0)
stack - libc:	[0x15d3d9000, 0x103cd61b000] (0x10270242000)
stack - ld:	[0x15d010000, 0x103cd252000] (0x10270242000)
$ gcc -m32 a.c -o x86
$ ./a.py ./x86
text:	[0x8048000, 0x8048000] (0x0)
heap:	[0x8058000, 0xa03c000] (0x1fe4000)
libc:	[0xf74fa000, 0xf75f9000] (0xff000)
ld:	[0xf76db000, 0xf77da000] (0xff000)
stack:	[0xff7df000, 0xfffdc000] (0x7fd000)
heap - text:	[0x10000, 0x1ff4000] (0x1fe4000)
libc - text:	[0xef4b2000, 0xef5b1000] (0xff000)
ld - text:	[0xef693000, 0xef792000] (0xff000)
stack - text:	[0xf7797000, 0xf7f94000] (0x7fd000)
libc - heap:	[0xed4e7000, 0xef559000] (0x2072000)
ld - heap:	[0xed6c8000, 0xef73a000] (0x2072000)
stack - heap:	[0xf57f3000, 0xf7f18000] (0x2725000)
ld - libc:	[0x1e1000, 0x1e1000] (0x0)
stack - libc:	[0x8208000, 0x8abf000] (0x8b7000)
stack - ld:	[0x8027000, 0x88de000] (0x8b7000)
$ gcc -fPIE -pie a.c -o x64pie
$ ./a.py ./x64pie
text:   [0x5555ad0d9000, 0x5655488f9000] (0xff9b820000)
heap:   [0x5555af197000, 0x5655496f3000] (0xff9a55c000)
libc:   [0x7efbf9679000, 0x7ffbcf5d1000] (0xffd5f58000)
ld:     [0x7efbf9a42000, 0x7ffbcf99a000] (0xffd5f58000)
stack:  [0x7ffc00658000, 0x7ffffef0e000] (0x3fe8b6000)
heap - text:    [0x217000, 0x21fb000] (0x1fe4000)
libc - text:    [0x28b3a813f000, 0x2a9ee4af7000] (0x1eb3c9b8000)
ld - text:      [0x28b3a8508000, 0x2a9ee4ec0000] (0x1eb3c9b8000)
stack - text:   [0x29a82af0d000, 0x2aa94fdbb000] (0x10124eae000)
libc - heap:    [0x28b3a735a000, 0x2a9ee383c000] (0x1eb3c4e2000)
ld - heap:      [0x28b3a7723000, 0x2a9ee3c05000] (0x1eb3c4e2000)
stack - heap:   [0x29a82a4ac000, 0x2aa94dcfd000] (0x10123851000)
ld - libc:      [0x3c9000, 0x3c9000] (0x0)
stack - libc:   [0xeeba8000, 0x10238acd000] (0x10149f25000)
stack - ld:     [0xee7df000, 0x10238704000] (0x10149f25000)
$ gcc -m32 -fPIE -pie a.c -o x86pie
$ ./a.py ./x86pie
text:   [0x56555000, 0x56654000] (0xff000)
heap:   [0x56569000, 0x58639000] (0x20d0000)
libc:   [0xf74fa000, 0xf75f9000] (0xff000)
ld:     [0xf76db000, 0xf77da000] (0xff000)
stack:  [0xff7dc000, 0xfffda000] (0x7fe000)
heap - text:    [0x13000, 0x1ffa000] (0x1fe7000)
libc - text:    [0xa0eb2000, 0xa1093000] (0x1e1000)
ld - text:      [0xa1093000, 0xa1274000] (0x1e1000)
stack - text:   [0xa91aa000, 0xa9a78000] (0x8ce000)
libc - heap:    [0x9ef37000, 0xa0fe6000] (0x20af000)
ld - heap:      [0x9f118000, 0xa11c7000] (0x20af000)
stack - heap:   [0xa725e000, 0xa993d000] (0x26df000)
ld - libc:      [0x1e1000, 0x1e1000] (0x0)
stack - libc:   [0x8209000, 0x8ad2000] (0x8c9000)
stack - ld:     [0x8028000, 0x88f1000] (0x8c9000)
#include <stdio.h>
int main(void) {
    printf("Hello, world!\n");
    scanf("%*c");
    return 0;
}
#!/usr/bin/env python3
import sys
import os
import re
import time
import subprocess

def vmmap(s, log=False):
    acc = []
    with subprocess.Popen([ s ], stdout=subprocess.DEVNULL) as p:
        time.sleep(0.1)
        path = '/proc/%d/maps' % p.pid
        if log:
            sys.stderr.write('$ cat %s\n' % path)
        with open(path) as fh:
            for line in fh:
                if log:
                    sys.stderr.write(line)
                addr, permission, _, _, _, *name = line.split()
                addr = tuple(map(lambda s: int(s, 16), addr.split('-')))
                name = name[0] if name else None
                acc += [( addr, permission, name )]
        p.kill()
    return acc

def gather(command, n, log=False):
    f = {}
    f['text'] = []
    f['heap'] = []
    f['libc'] = []
    f['ld'] = []
    f['stack'] = []
    for i in range(n):
        for addr, permission, name in vmmap(command, log=log):
            if permission == 'r-xp' and name.endswith(os.path.basename(command)):
                f['text'] += [ addr[0] ]
            if name == '[heap]':
                f['heap'] += [ addr[0] ]
            if permission == 'r-xp' and re.search(r'/libc-\d\.\d\d\.so$', name):
                f['libc'] += [ addr[0] ]
            if permission == 'r-xp' and re.search(r'/ld-\d\.\d\d\.so$', name):
                f['ld'] += [ addr[0] ]
            if name == '[stack]':
                f['stack'] += [ addr[0] ]
    return f

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('command')
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('-n', type=int, default=1024)
args = parser.parse_args()

f = gather(args.command, args.n, log=args.verbose)
ks = sorted(f.keys(), key=lambda k: min(f[k]))
for k in ks:
    l, r = min(f[k]), max(f[k])
    print('%s:\t[%#x, %#x] (%#x)' % (k, l, r, r-l))
for k1 in ks:
    for k2 in ks:
        if ks.index(k1) < ks.index(k2):
            diff = list(map(lambda x1, x2: x2 - x1, f[k1], f[k2]))
            l, r = min(diff), max(diff)
            print('%s - %s:\t[%#x, %#x] (%#x)' % (k2, k1, l, r, r-l))