Revamp do_xmap() to work under exec-shield and exec-shield-randomize,
and with pre-linked PT_INTERP such as ld-linux.so.2. l_lx_elf.c l_lx_sh.c committer: jreiser <jreiser> 1084923593 +0000
This commit is contained in:
+77
-50
@@ -236,44 +236,87 @@ auxv_up(Elf32_auxv_t *av, int const type, unsigned const value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The PF_* and PROT_* bits are {1,2,4}; the conversion table fits in 32 bits.
|
||||||
|
#define REP8(x) \
|
||||||
|
((x)|((x)<<4)|((x)<<8)|((x)<<12)|((x)<<16)|((x)<<20)|((x)<<24)|((x)<<28))
|
||||||
|
#define EXP8(y) \
|
||||||
|
((1&(y)) ? 0xf0f0f0f0 : (2&(y)) ? 0xff00ff00 : (4&(y)) ? 0xffff0000 : 0)
|
||||||
|
|
||||||
|
static inline unsigned
|
||||||
|
__attribute__((regparm(1), stdcall))
|
||||||
|
pf_to_prot(unsigned const pf)
|
||||||
|
{
|
||||||
|
return (PROT_READ|PROT_WRITE|PROT_EXEC) & (
|
||||||
|
( (REP8(PROT_EXEC ) & EXP8(PF_X))
|
||||||
|
|(REP8(PROT_READ ) & EXP8(PF_R))
|
||||||
|
|(REP8(PROT_WRITE) & EXP8(PF_W))
|
||||||
|
) >> ((pf & (PF_R|PF_W|PF_X))<<2) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Find convex hull of PT_LOAD (the minimal interval which covers all PT_LOAD),
|
||||||
|
// and mmap that much, to be sure that a kernel using exec-shield-randomize
|
||||||
|
// won't place the first piece in a way that leaves no room for the rest.
|
||||||
|
static unsigned long // returns relocation constant
|
||||||
|
__attribute__((regparm(3), stdcall))
|
||||||
|
xfind_pages(unsigned mflags, Elf32_Phdr const *phdr, int phnum,
|
||||||
|
char **const p_brk
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t lo= ~0, hi= 0, szlo= 0;
|
||||||
|
char *addr;
|
||||||
|
mflags += MAP_PRIVATE | MAP_ANONYMOUS; // '+' can optimize better than '|'
|
||||||
|
for (; --phnum>=0; ++phdr) if (PT_LOAD==phdr->p_type) {
|
||||||
|
if (phdr->p_vaddr < lo) {
|
||||||
|
lo = phdr->p_vaddr;
|
||||||
|
szlo = phdr->p_filesz;
|
||||||
|
}
|
||||||
|
if (hi < (phdr->p_memsz + phdr->p_vaddr)) {
|
||||||
|
hi = phdr->p_memsz + phdr->p_vaddr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
szlo += ~PAGE_MASK & lo; // page fragment on lo edge
|
||||||
|
lo -= ~PAGE_MASK & lo; // round down to page boundary
|
||||||
|
hi = PAGE_MASK & (hi - lo - PAGE_MASK -1); // page length
|
||||||
|
szlo = PAGE_MASK & (szlo - PAGE_MASK -1); // page length
|
||||||
|
addr = do_mmap((void *)lo, hi, PROT_READ|PROT_WRITE|PROT_EXEC, mflags, 0, 0);
|
||||||
|
*p_brk = hi + addr; // the logical value of brk(0)
|
||||||
|
munmap(szlo + addr, hi - szlo); // desirable if PT_LOAD non-contiguous
|
||||||
|
return (unsigned long)addr - lo;
|
||||||
|
}
|
||||||
|
|
||||||
static Elf32_Addr // entry address
|
static Elf32_Addr // entry address
|
||||||
do_xmap(int const fdi, Elf32_Ehdr const *const ehdr, struct Extent *const xi,
|
do_xmap(int const fdi, Elf32_Ehdr const *const ehdr, struct Extent *const xi,
|
||||||
Elf32_auxv_t *const av)
|
Elf32_auxv_t *const av)
|
||||||
{
|
{
|
||||||
Elf32_Phdr const *phdr = (Elf32_Phdr const *) (ehdr->e_phoff +
|
Elf32_Phdr const *phdr = (Elf32_Phdr const *) (ehdr->e_phoff +
|
||||||
(char const *)ehdr);
|
(char const *)ehdr);
|
||||||
unsigned long base = (ET_DYN==ehdr->e_type) ? 0x40000000 : 0;
|
char *v_brk;
|
||||||
|
unsigned long const reloc = xfind_pages(
|
||||||
|
((ET_DYN!=ehdr->e_type) ? MAP_FIXED : 0), phdr, ehdr->e_phnum, &v_brk);
|
||||||
int j;
|
int j;
|
||||||
for (j=0; j < ehdr->e_phnum; ++phdr, ++j)
|
for (j=0; j < ehdr->e_phnum; ++phdr, ++j)
|
||||||
if (PT_PHDR==phdr->p_type) {
|
if (PT_PHDR==phdr->p_type) {
|
||||||
auxv_up(av, AT_PHDR, phdr->p_vaddr);
|
auxv_up(av, AT_PHDR, phdr->p_vaddr + reloc);
|
||||||
}
|
}
|
||||||
else if (PT_LOAD==phdr->p_type) {
|
else if (PT_LOAD==phdr->p_type) {
|
||||||
|
unsigned const prot = pf_to_prot(phdr->p_flags);
|
||||||
struct Extent xo;
|
struct Extent xo;
|
||||||
size_t mlen = xo.size = phdr->p_filesz;
|
size_t mlen = xo.size = phdr->p_filesz;
|
||||||
char *addr = xo.buf = (char *)phdr->p_vaddr;
|
char *addr = xo.buf = (char *)phdr->p_vaddr;
|
||||||
char *haddr = phdr->p_memsz + (char *)phdr->p_vaddr;
|
char *haddr = phdr->p_memsz + addr;
|
||||||
size_t frag = (int)addr &~ PAGE_MASK;
|
size_t frag = (int)addr &~ PAGE_MASK;
|
||||||
mlen += frag;
|
mlen += frag;
|
||||||
addr -= frag;
|
addr -= frag;
|
||||||
if (ET_DYN==ehdr->e_type) {
|
addr += reloc;
|
||||||
addr += base;
|
haddr += reloc;
|
||||||
haddr += base;
|
|
||||||
}
|
|
||||||
else { // There is only one brk, the one for the ET_EXEC
|
|
||||||
// Not needed if compressed a.elf is invoked directly.
|
|
||||||
// Needed only if compressed shell script invokes compressed shell.
|
|
||||||
do_brk(haddr+OVERHEAD); // Also takes care of whole pages of .bss
|
|
||||||
}
|
|
||||||
// Decompressor can overrun the destination by 3 bytes.
|
// Decompressor can overrun the destination by 3 bytes.
|
||||||
if (addr != do_mmap(addr, mlen + (xi ? 3 : 0), PROT_READ | PROT_WRITE,
|
if (addr != do_mmap(addr, mlen + (xi ? 3 : 0), PROT_READ | PROT_WRITE,
|
||||||
MAP_FIXED | MAP_PRIVATE | (xi ? MAP_ANONYMOUS : 0),
|
MAP_FIXED | MAP_PRIVATE | (xi ? MAP_ANONYMOUS : 0),
|
||||||
fdi, phdr->p_offset - frag) ) {
|
fdi, phdr->p_offset - frag) ) {
|
||||||
err_exit(8);
|
err_exit(8);
|
||||||
}
|
}
|
||||||
if (0==base) {
|
|
||||||
base = (unsigned long)addr;
|
|
||||||
}
|
|
||||||
if (xi) {
|
if (xi) {
|
||||||
unpackExtent(xi, &xo, (f_expand *)fdi,
|
unpackExtent(xi, &xo, (f_expand *)fdi,
|
||||||
((phdr->p_flags & PF_X) ? (f_unfilter *)(2+ fdi) : 0));
|
((phdr->p_flags & PF_X) ? (f_unfilter *)(2+ fdi) : 0));
|
||||||
@@ -288,52 +331,36 @@ do_xmap(int const fdi, Elf32_Ehdr const *const ehdr, struct Extent *const xi,
|
|||||||
auxv_up((Elf32_auxv_t *)(~1 & (int)av), AT_NULL, (unsigned)hatch);
|
auxv_up((Elf32_auxv_t *)(~1 & (int)av), AT_NULL, (unsigned)hatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (phdr->p_memsz != phdr->p_filesz) { // .bss
|
if (0!=mprotect(addr, mlen, prot)) {
|
||||||
if (ET_DYN==ehdr->e_type) { // PT_INTERP whole pages of .bss?
|
err_exit(10);
|
||||||
addr += frag + mlen;
|
|
||||||
mlen = haddr - addr;
|
|
||||||
if (0 < (int)mlen) { // need more pages, too
|
|
||||||
if (addr != do_mmap(addr, mlen, PROT_READ | PROT_WRITE,
|
|
||||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 ) ) {
|
|
||||||
err_exit(9);
|
|
||||||
ERR_LAB
|
ERR_LAB
|
||||||
}
|
}
|
||||||
}
|
addr += mlen + frag; /* page boundary on hi end */
|
||||||
|
if (addr < haddr) { // need pages for .bss
|
||||||
|
if (addr != do_mmap(addr, haddr - addr, prot,
|
||||||
|
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 ) ) {
|
||||||
|
err_exit(9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // no .bss
|
else if (xi) { // cleanup if decompressor overrun crosses page boundary
|
||||||
int prot = 0;
|
mlen = ~PAGE_MASK & (3+ mlen);
|
||||||
if (phdr->p_flags & PF_X) { prot |= PROT_EXEC; }
|
if (mlen<=3) { // page fragment was overrun buffer only
|
||||||
if (phdr->p_flags & PF_W) { prot |= PROT_WRITE; }
|
munmap(addr, mlen);
|
||||||
if (phdr->p_flags & PF_R) { prot |= PROT_READ; }
|
|
||||||
if (0!=mprotect(addr, mlen, prot)) {
|
|
||||||
err_exit(10);
|
|
||||||
}
|
}
|
||||||
if (xi) { // cleanup if decompressor overrun crosses page boundary
|
|
||||||
mlen += 3;
|
|
||||||
addr += mlen;
|
|
||||||
mlen &= ~PAGE_MASK;
|
|
||||||
if (mlen<=3) { // page fragment was overrun buffer only
|
|
||||||
munmap(addr - mlen, mlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ET_DYN!=ehdr->e_type) {
|
|
||||||
// Needed only if compressed shell script invokes compressed shell.
|
|
||||||
do_brk(haddr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!xi) {
|
if (!xi) { // 2nd call (PT_INTERP); close()+check is smaller here
|
||||||
if (0!=close(fdi)) {
|
if (0!=close(fdi)) {
|
||||||
err_exit(11);
|
err_exit(11);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ET_DYN==ehdr->e_type) {
|
else { // 1st call (main); also have (0!=av) here
|
||||||
return ehdr->e_entry + base;
|
if (ET_DYN!=ehdr->e_type) {
|
||||||
}
|
// Needed only if compressed shell script invokes compressed shell.
|
||||||
else {
|
do_brk(v_brk);
|
||||||
return ehdr->e_entry;
|
}
|
||||||
}
|
}
|
||||||
|
return ehdr->e_entry + reloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+81
-45
@@ -168,11 +168,72 @@ bzero(char *p, size_t len)
|
|||||||
} while (--len);
|
} while (--len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define REP8(x) \
|
||||||
|
((x)|((x)<<4)|((x)<<8)|((x)<<12)|((x)<<16)|((x)<<20)|((x)<<24)|((x)<<28))
|
||||||
|
#define EXP8(y) \
|
||||||
|
((1&(y)) ? 0xf0f0f0f0 : (2&(y)) ? 0xff00ff00 : (4&(y)) ? 0xffff0000 : 0)
|
||||||
|
|
||||||
|
static inline unsigned
|
||||||
|
__attribute__((regparm(1), stdcall))
|
||||||
|
pf_to_prot(unsigned const pf)
|
||||||
|
{
|
||||||
|
return 7 & (( (REP8(PROT_EXEC ) & EXP8(PF_X))
|
||||||
|
|(REP8(PROT_READ ) & EXP8(PF_R))
|
||||||
|
|(REP8(PROT_WRITE) & EXP8(PF_W)) )
|
||||||
|
>> ((pf & (PF_R|PF_W|PF_X))<<2) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Find convex hull of PT_LOAD (the minimal interval which covers all PT_LOAD),
|
||||||
|
// and mmap that much, to be sure that a kernel using exec-shield-randomize
|
||||||
|
// won't place the first piece in a way that leaves no room for the rest.
|
||||||
|
static unsigned long // returns relocation constant
|
||||||
|
__attribute__((regparm(3), stdcall))
|
||||||
|
xfind_pages(unsigned mflags, Elf32_Phdr const *phdr, int phnum)
|
||||||
|
{
|
||||||
|
size_t lo= ~0, hi= 0, szlo= 0;
|
||||||
|
char *addr;
|
||||||
|
mflags += MAP_PRIVATE | MAP_ANONYMOUS; // '+' can optimize better than '|'
|
||||||
|
for (; --phnum>=0; ++phdr) if (PT_LOAD==phdr->p_type) {
|
||||||
|
if (phdr->p_vaddr < lo) {
|
||||||
|
lo = phdr->p_vaddr;
|
||||||
|
szlo = phdr->p_filesz;
|
||||||
|
}
|
||||||
|
if (hi < (phdr->p_memsz + phdr->p_vaddr)) {
|
||||||
|
hi = phdr->p_memsz + phdr->p_vaddr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (MAP_FIXED & mflags) { // the "shell", and not the PT_INTERP
|
||||||
|
// This is a dirty hack to set the proper value for brk(0) as seen by
|
||||||
|
// the "shell" which we will mmap() soon, upon return to do_xmap().
|
||||||
|
// It depends on our own brk() starting out at 0x08048000, which is the
|
||||||
|
// default base address used by /bin/ld for an ET_EXEC. We must brk()
|
||||||
|
// now. If we wait until after mmap() of shell pages, then the kernel
|
||||||
|
// "vma" containing our original brk() of 0x08048000 will not be contiguous
|
||||||
|
// with hi [the mmap'ed pages from the shell will be in between],
|
||||||
|
// and various linux kernels will not move the brk() in this case;
|
||||||
|
// the typical symptom is SIGSEGV early in ld-linux.so.2 (the PT_INTERP).
|
||||||
|
do_brk((void *)hi);
|
||||||
|
}
|
||||||
|
szlo += ~PAGE_MASK & lo; // page fragment on lo edge
|
||||||
|
lo -= ~PAGE_MASK & lo; // round down to page boundary
|
||||||
|
hi = PAGE_MASK & (hi - lo - PAGE_MASK -1); // page length
|
||||||
|
szlo = PAGE_MASK & (szlo - PAGE_MASK -1); // page length
|
||||||
|
addr = do_mmap((void *)lo, hi, PROT_READ|PROT_WRITE|PROT_EXEC, mflags, 0, 0);
|
||||||
|
|
||||||
|
// Doing this may destroy the brk() that we set so carefully above.
|
||||||
|
// The munmap() is "needed" only for discontiguous PT_LOAD,
|
||||||
|
// and neither shells nor ld-linux.so.2 have that.
|
||||||
|
// munmap(szlo + addr, hi - szlo);
|
||||||
|
|
||||||
|
return (unsigned long)addr - lo;
|
||||||
|
}
|
||||||
|
|
||||||
// This do_xmap() has no Extent *xi input because it doesn't decompress anything;
|
// This do_xmap() has no Extent *xi input because it doesn't decompress anything;
|
||||||
// it only maps the shell and its PT_INTERP. So, it was specialized by hand
|
// it only maps the shell and its PT_INTERP. So, it was specialized by hand
|
||||||
// to reduce compiled instruction size. gdb 2.91.66 does not notice that
|
// to reduce compiled instruction size. gcc 2.91.66 does not notice that
|
||||||
// there is only one call to this static function (from getexec(), which
|
// there is only one call to this static function (from getexec(), which
|
||||||
// would specify 0 for xi), so gdb does not propagate the constant parameter.
|
// would specify 0 for xi), so gcc does not propagate the constant parameter.
|
||||||
// Notice there is no make_hatch(), either.
|
// Notice there is no make_hatch(), either.
|
||||||
|
|
||||||
static Elf32_Addr // entry address
|
static Elf32_Addr // entry address
|
||||||
@@ -180,77 +241,52 @@ do_xmap(int const fdi, Elf32_Ehdr const *const ehdr, Elf32_auxv_t *const av)
|
|||||||
{
|
{
|
||||||
Elf32_Phdr const *phdr = (Elf32_Phdr const *) (ehdr->e_phoff +
|
Elf32_Phdr const *phdr = (Elf32_Phdr const *) (ehdr->e_phoff +
|
||||||
(char const *)ehdr);
|
(char const *)ehdr);
|
||||||
unsigned long base = (ET_DYN==ehdr->e_type) ? 0x40000000 : 0;
|
unsigned long const reloc = xfind_pages(
|
||||||
|
((ET_DYN!=ehdr->e_type) ? MAP_FIXED : 0), phdr, ehdr->e_phnum);
|
||||||
int j;
|
int j;
|
||||||
for (j=0; j < ehdr->e_phnum; ++phdr, ++j)
|
for (j=0; j < ehdr->e_phnum; ++phdr, ++j)
|
||||||
if (PT_PHDR==phdr->p_type) {
|
if (PT_PHDR==phdr->p_type) {
|
||||||
av[AT_PHDR -1].a_un.a_val = phdr->p_vaddr;
|
av[AT_PHDR -1].a_un.a_val = phdr->p_vaddr;
|
||||||
}
|
}
|
||||||
else if (PT_LOAD==phdr->p_type) {
|
else if (PT_LOAD==phdr->p_type) {
|
||||||
|
unsigned const prot = pf_to_prot(phdr->p_flags);
|
||||||
struct Extent xo;
|
struct Extent xo;
|
||||||
size_t mlen = xo.size = phdr->p_filesz;
|
size_t mlen = xo.size = phdr->p_filesz;
|
||||||
char *addr = xo.buf = (char *)phdr->p_vaddr;
|
char *addr = xo.buf = (char *)phdr->p_vaddr;
|
||||||
char *haddr = phdr->p_memsz + (char *)phdr->p_vaddr;
|
char *haddr = phdr->p_memsz + addr;
|
||||||
size_t frag = (int)addr &~ PAGE_MASK;
|
size_t frag = (int)addr &~ PAGE_MASK;
|
||||||
mlen += frag;
|
mlen += frag;
|
||||||
addr -= frag;
|
addr -= frag;
|
||||||
if (ET_DYN==ehdr->e_type) {
|
addr += reloc;
|
||||||
addr += base;
|
haddr += reloc;
|
||||||
haddr += base;
|
|
||||||
}
|
|
||||||
else { // There is only one brk, the one for the ET_EXEC
|
|
||||||
do_brk(haddr+OVERHEAD); // Also takes care of whole pages of .bss
|
|
||||||
}
|
|
||||||
// Decompressor can overrun the destination by 3 bytes.
|
// Decompressor can overrun the destination by 3 bytes.
|
||||||
if (addr != do_mmap(addr, mlen, PROT_READ | PROT_WRITE,
|
if (addr != do_mmap(addr, mlen, PROT_READ | PROT_WRITE,
|
||||||
MAP_FIXED | MAP_PRIVATE,
|
MAP_FIXED | MAP_PRIVATE,
|
||||||
fdi, phdr->p_offset - frag) ) {
|
fdi, phdr->p_offset - frag) ) {
|
||||||
err_exit(8);
|
err_exit(8);
|
||||||
}
|
}
|
||||||
if (0==base) {
|
|
||||||
base = (unsigned long)addr;
|
|
||||||
}
|
|
||||||
bzero(addr, frag); // fragment at lo end
|
bzero(addr, frag); // fragment at lo end
|
||||||
frag = (-mlen) &~ PAGE_MASK; // distance to next page boundary
|
frag = (-mlen) &~ PAGE_MASK; // distance to next page boundary
|
||||||
bzero(mlen+addr, frag); // fragment at hi end
|
bzero(mlen+addr, frag); // fragment at hi end
|
||||||
if (phdr->p_memsz != phdr->p_filesz) { // .bss
|
if (0!=mprotect(addr, mlen, prot)) {
|
||||||
if (ET_DYN==ehdr->e_type) { // PT_INTERP whole pages of .bss?
|
err_exit(10);
|
||||||
addr += frag + mlen;
|
|
||||||
mlen = haddr - addr;
|
|
||||||
if (0 < (int)mlen) { // need more pages, too
|
|
||||||
if (addr != do_mmap(addr, mlen, PROT_READ | PROT_WRITE,
|
|
||||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 ) ) {
|
|
||||||
err_exit(9);
|
|
||||||
ERR_LAB
|
ERR_LAB
|
||||||
}
|
}
|
||||||
}
|
addr += mlen + frag; /* page boundary on hi end */
|
||||||
|
if (addr < haddr) { // need pages for .bss
|
||||||
|
if (addr != do_mmap(addr, haddr - addr, prot,
|
||||||
|
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 ) ) {
|
||||||
|
err_exit(9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // no .bss
|
|
||||||
int prot = 0;
|
|
||||||
if (phdr->p_flags & PF_X) { prot |= PROT_EXEC; }
|
|
||||||
if (phdr->p_flags & PF_W) { prot |= PROT_WRITE; }
|
|
||||||
if (phdr->p_flags & PF_R) { prot |= PROT_READ; }
|
|
||||||
if (0!=mprotect(addr, mlen, prot)) {
|
|
||||||
err_exit(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ET_DYN!=ehdr->e_type) {
|
|
||||||
do_brk(haddr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (0!=close(fdi)) {
|
if (0!=close(fdi)) {
|
||||||
err_exit(11);
|
err_exit(11);
|
||||||
}
|
|
||||||
if (ET_DYN==ehdr->e_type) {
|
|
||||||
return ehdr->e_entry + base;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return ehdr->e_entry;
|
|
||||||
}
|
}
|
||||||
|
return ehdr->e_entry + reloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Elf32_Addr // entry address
|
static Elf32_Addr // entry address
|
||||||
getexec(char const *const fname, Elf32_Ehdr *const ehdr, Elf32_auxv_t *const av)
|
getexec(char const *const fname, Elf32_Ehdr *const ehdr, Elf32_auxv_t *const av)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user