Put upx stub loader at high end of ELF output file on linux,

and allow for block-by-block specification of filter and parameters.
	linker.cpp linker.h mem.cpp mem.h p_elf.h
	p_lx_elf.cpp p_lx_elf.h
	p_lx_exc.cpp p_lx_exc.h
	p_lx_sh.cpp  p_lx_sh.h
	p_unix.cpp p_unix.h
	packer.cpp packer.h
	stub/fold_elf86.asm stub/fold_exec86.asm stub/fold_sh86.asm
	stub/l_lx_elf.c  stub/l_lx_elf86.asm  stub/l_lx_elf86.lds
	stub/l_lx_exec.c stub/l_lx_exec86.asm stub/l_lx_exec86.lds
	stub/l_lx_sh.c   stub/l_lx_sh86.asm   stub/l_lx_sh86.lds
	stub/linux.hh

committer: jreiser <jreiser> 981084316 +0000
This commit is contained in:
John Reiser
2001-02-02 03:25:16 +00:00
parent 9005e50049
commit 40fddf1715
28 changed files with 797 additions and 614 deletions
+261 -84
View File
@@ -38,6 +38,11 @@
#include "p_lx_exc.h"
#define PT_LOAD Elf_LE32_Phdr::PT_LOAD
#define PT_DYNAMIC Elf_LE32_Phdr::PT_DYNAMIC
#define DT_NULL Elf_LE32_Dyn::DT_NULL
#define DT_NEEDED Elf_LE32_Dyn::DT_NEEDED
#define DT_STRTAB Elf_LE32_Dyn::DT_STRTAB
#define DT_STRSZ Elf_LE32_Dyn::DT_STRSZ
/*************************************************************************
@@ -75,11 +80,225 @@ PackLinuxI386::getFilters() const
return filters;
}
struct cprElfhdr {
struct Elf_LE32_Ehdr ehdr;
struct Elf_LE32_Phdr phdr[2];
struct PackUnix::l_info linfo;
};
Elf_LE32_Phdr const *
PackLinuxI386::find_DYNAMIC(Elf_LE32_Phdr const *phdr, unsigned n)
{
if (n) do if (PT_DYNAMIC==phdr->p_type) {
return phdr;
} while (++phdr, 0!=--n);
return 0;
}
unsigned
PackLinuxI386::find_file_offset(
unsigned const vaddr,
Elf_LE32_Phdr const *phdr,
unsigned e_phnum
)
{
if (e_phnum) do {
unsigned t = vaddr - phdr->p_vaddr;
if (t < phdr->p_memsz) {
return t + phdr->p_offset;
}
} while (++phdr, 0!=--e_phnum);
return ~0u;
}
void
PackLinuxI386::generateElfHdr(
OutputFile *const fo,
void const *const proto,
Elf_LE32_Phdr const *const phdr0,
unsigned e_phnum,
unsigned const brka
)
{
cprElfHdr1 *const h1 = (cprElfHdr1 *)&elfout;
cprElfHdr2 *const h2 = (cprElfHdr2 *)&elfout;
cprElfHdr3 *const h3 = (cprElfHdr3 *)&elfout;
memcpy(h2, proto, sizeof(*h2));
assert(h2->ehdr.e_phoff == sizeof(Elf_LE32_Ehdr));
assert(h2->ehdr.e_shoff == 0);
assert(h2->ehdr.e_ehsize == sizeof(Elf_LE32_Ehdr));
assert(h2->ehdr.e_phentsize == sizeof(Elf_LE32_Phdr));
assert(h2->ehdr.e_shnum == 0);
#if 0 //{
unsigned identsize;
char const *const ident = identstr(identsize);
#endif //}
h2->phdr[0].p_filesz = sizeof(*h2); // + identsize;
h2->phdr[0].p_memsz = h2->phdr[0].p_filesz;
// Info for OS kernel to set the brk()
if (brka) {
h2->phdr[1].p_type = PT_LOAD; // be sure
h2->phdr[1].p_offset = 0xfff&brka;
h2->phdr[1].p_vaddr = brka;
h2->phdr[1].p_paddr = brka;
h2->phdr[1].p_filesz = 0;
h2->phdr[1].p_memsz = 0;
}
if (ph.format==UPX_F_LINUX_i386 ) {
assert(h1->ehdr.e_phnum==1);
memset(&h1->linfo, 0, sizeof(h1->linfo));
fo->write(h1, sizeof(*h1));
}
else if (ph.format==UPX_F_LINUX_ELF_i386) {
int const j = ((char *)&h3->phdr[2]) - (char *)h3;
memcpy(&h3->phdr[2], j + (char const *)proto, sizeof(h3->phdr[2]));
assert(h3->ehdr.e_phnum==3);
memset(&h3->linfo, 0, sizeof(h3->linfo));
fo->write(h3, sizeof(*h3));
}
else if (ph.format==UPX_F_LINUX_SH_i386) {
assert(h2->ehdr.e_phnum==1);
h2->ehdr.e_phnum = 2;
memset(&h2->linfo, 0, sizeof(h2->linfo));
fo->write(h2, sizeof(*h2));
}
else {
assert(false); // unknown ph.format, PackUnix::generateElfHdr
}
Elf_LE32_Phdr const *const phdrdyn = find_DYNAMIC(phdr0, e_phnum);
if (phdrdyn) { // propagate DT_NEEDED
MemBuffer dynhdr(phdrdyn->p_memsz);
fi->seek(phdrdyn->p_offset, SEEK_SET);
fi->read(dynhdr, phdrdyn->p_memsz);
unsigned strtabx = ~0u;
unsigned strsz = 0;
int j;
Elf_LE32_Dyn const *p = (Elf_LE32_Dyn const *)(unsigned char *)dynhdr;
int so_needed = 0;
for (j = phdrdyn->p_memsz / sizeof(*p); --j>=0; ++p) {
if (p->d_tag==DT_NEEDED) {
so_needed++;
}
if (p->d_tag==DT_STRTAB) {
strtabx = find_file_offset(p->d_val, phdr0, e_phnum);
}
if (p->d_tag==DT_STRSZ) {
strsz= p->d_val;
}
}
if (so_needed) {
assert(0!=strsz && ~0u!=strtabx);
MemBuffer strtab(strsz);
fi->seek(strtabx, SEEK_SET);
fi->read(strtab, strsz);
int c_needed = 1; // index 0 is reserved
p = (Elf_LE32_Dyn const *)(unsigned char *)dynhdr;
for (j = phdrdyn->p_memsz / sizeof(*p); --j>=0; ++p) {
if (p->d_tag==DT_NEEDED) {
c_needed += 1+ strlen(p->d_val + strtab);
}
}
MemBuffer newtab(c_needed);
unsigned char *cp = newtab;
*cp++ = 0;
p = (Elf_LE32_Dyn const *)(unsigned char *)dynhdr;
for (j = phdrdyn->p_memsz / sizeof(*p); --j>=0; ++p) {
if (p->d_tag==DT_NEEDED) {
unsigned char const *const str = p->d_val + strtab;
unsigned const len = 1+ strlen(str);
memcpy(cp, str, len);
cp += len;
}
}
Elf_LE32_Dyn outdyn[3 + so_needed], *q = &outdyn[0];
q->d_tag = DT_STRSZ;
q->d_val = c_needed; // + identsize;
++q;
q->d_tag = DT_STRTAB;
q->d_val = sizeof(*h3) + sizeof(outdyn) + h3->phdr[0].p_vaddr;
++q;
cp = 1+ newtab;
for (j= so_needed; --j>=0; ++q) {
q->d_tag = DT_NEEDED; q->d_val = cp - newtab;
cp += 1+ strlen(cp);
}
q->d_tag = DT_NULL; q->d_val = 0;
h3->phdr[2].p_type = PT_DYNAMIC;
h3->phdr[2].p_offset = sizeof(*h3);
h3->phdr[2].p_vaddr = sizeof(*h3) + h3->phdr[0].p_vaddr;
h3->phdr[2].p_paddr = h3->phdr[2].p_vaddr;
h3->phdr[2].p_filesz = sizeof(outdyn) + c_needed; // + identsize;
h3->phdr[2].p_memsz = h3->phdr[2].p_filesz;
h3->phdr[2].p_flags = Elf_LE32_Phdr::PF_R;
h3->phdr[2].p_align = 4;
pt_dynamic.alloc(h3->phdr[2].p_filesz);
j = 0;
memcpy(j + pt_dynamic, &outdyn[0], sizeof(outdyn));
j += sizeof(outdyn);
memcpy(j + pt_dynamic, newtab, c_needed);
j += c_needed;
#if 0 //{
memcpy(j + pt_dynamic, ident, identsize);
// FIXME ?? patchVersion(j + pt_dynamic, identsize);
j += identsize;
#endif //}
sz_dynamic = j;
#if 0 //{ debugging: see the results early
fo->seek(0, SEEK_SET);
fo->write(h3, sizeof(*h3));
#endif //}
fo->write(pt_dynamic, sz_dynamic);
}
}
}
void
PackLinuxI386::pack1(OutputFile *fo, Filter &)
{
// create a pseudo-unique program id for our paranoid stub
progid = getRandomId();
generateElfHdr(fo, linux_i386exec_fold, 0, 0, 0);
}
void
PackLinuxI386::pack4(OutputFile *fo, Filter &ft)
{
overlay_offset = sizeof(elfout.ehdr) +
(elfout.ehdr.e_phentsize * elfout.ehdr.e_phnum) +
sizeof(l_info) +
((elfout.ehdr.e_phnum==3) ? elfout.phdr[2].p_memsz : 0) ;
super::pack4(fo, ft); // write PackHeader and overlay_offset
elfout.phdr[0].p_filesz = fo->getBytesWritten();
#define PAGE_MASK (~0<<12)
// pre-calculate for benefit of runtime disappearing act via munmap()
elfout.phdr[0].p_memsz = PAGE_MASK & (~PAGE_MASK + elfout.phdr[0].p_filesz);
#undef PAGE_MASK
// rewrite Elf header
fo->seek(0, SEEK_SET);
fo->rewrite(&elfout, sizeof(elfout.ehdr) +
elfout.ehdr.e_phnum * sizeof(elfout.phdr[0]) +
sizeof(l_info) );
}
static unsigned
umax(unsigned a, unsigned b)
{
if (a <= b) {
return b;
}
return a;
}
int
PackLinuxI386::buildLinuxLoader(
@@ -87,52 +306,22 @@ PackLinuxI386::buildLinuxLoader(
unsigned const szproto,
upx_byte const *const fold,
unsigned const szfold,
Filter const *ft,
unsigned const brka
Filter const *ft
)
{
initLoader(proto, szproto);
struct cprElfhdr elfhdr = *(struct cprElfhdr const *)fold;
memset(&elfhdr.linfo, 0, sizeof(elfhdr.linfo));
cprElfHdr1 const *const hf = (cprElfHdr1 const *)fold;
unsigned const fold_hdrlen = umax(0x80, sizeof(hf->ehdr) +
hf->ehdr.e_phentsize * hf->ehdr.e_phnum + sizeof(l_info) );
struct b_info h; memset(&h, 0, sizeof(h));
h.sz_unc = szfold - fold_hdrlen;
h.b_method = ph.method;
h.b_ftid = ph.filter;
h.b_cto8 = ph.filter_cto;
unsigned char const *const uncLoader = fold_hdrlen + fold;
// Info for OS kernel to set the brk()
if (brka) {
assert(ph.format==UPX_F_LINUX_ELF_i386
|| ph.format==UPX_F_LINUX_SH_i386 );
if (ph.format==UPX_F_LINUX_SH_i386) {
assert(elfhdr.ehdr.e_phnum==1);
elfhdr.ehdr.e_phnum = 2;
}
elfhdr.phdr[1].p_offset = 0xfff&brka;
elfhdr.phdr[1].p_vaddr = brka;
elfhdr.phdr[1].p_paddr = brka;
elfhdr.phdr[1].p_filesz = 0;
elfhdr.phdr[1].p_memsz = 0;
}
// The beginning of our loader consists of a elf_hdr (52 bytes) and
// two sections elf_phdr (2 * 32 byte), so we have 12 free bytes
// from offset 116 to the program start at offset 128.
assert(elfhdr.ehdr.e_phoff == sizeof(Elf_LE32_Ehdr));
assert(elfhdr.ehdr.e_shoff == 0);
assert(elfhdr.ehdr.e_ehsize == sizeof(Elf_LE32_Ehdr));
assert(elfhdr.ehdr.e_phentsize == sizeof(Elf_LE32_Phdr));
assert(elfhdr.ehdr.e_phnum == (unsigned)(1+ (0!=brka)));
assert(elfhdr.ehdr.e_shnum == 0);
linker->addSection("ELFHEADX", (unsigned char const *)&elfhdr, sizeof(elfhdr));
addLoader("ELFHEADX", 0);
struct {
upx_uint sz_unc; // uncompressed
upx_uint sz_cpr; // compressed
} h = {
szfold - sizeof(elfhdr), 0
};
unsigned char const *const uncLoader = (unsigned char const *)(1+
(struct cprElfhdr const *)fold );
unsigned char *const cprLoader = new unsigned char[sizeof(h) + h.sz_unc];
int r = upx_compress(uncLoader, h.sz_unc, sizeof(h) + cprLoader, &h.sz_cpr,
NULL, ph.method, 10, NULL, NULL );
if (r != UPX_E_OK || h.sz_cpr >= h.sz_unc)
@@ -143,7 +332,21 @@ PackLinuxI386::buildLinuxLoader(
n_mru = ft->n_mru;
addLoader("IDENTSTR", 0);
// Here is a quick summary of the format of the output file:
linker->setLoaderAlignOffset(
// Elf32_Edhr
sizeof(elfout.ehdr) +
// Elf32_Phdr: 1 for exec86, 2 for sh86, 3 for elf86
(elfout.ehdr.e_phentsize * elfout.ehdr.e_phnum) +
// checksum UPX! lsize version format
sizeof(l_info) +
// PT_DYNAMIC with DT_NEEDED "forwarded" from original file
((elfout.ehdr.e_phnum==3) ? elfout.phdr[2].p_memsz : 0) +
// p_progid, p_filesize, p_blocksize
sizeof(p_info) +
// compressed data
ph.b_len + ph.c_len );
// entry to stub
addLoader("LEXEC000", 0);
if (ft->id) {
@@ -206,21 +409,18 @@ PackLinuxI386::buildLinuxLoader(
}
}
addLoader("IDENTSTR", 0);
addLoader("LEXEC020", 0);
addLoader("FOLDEXEC", 0);
struct Elf_LE32_Ehdr *const ldrEhdr =
(struct Elf_LE32_Ehdr *)const_cast<unsigned char *>(getLoader());
unsigned e_entry = getLoaderSectionStart("LEXEC000");
ldrEhdr->e_entry = e_entry + elfhdr.phdr[0].p_vaddr;
char *ptr_cto = e_entry + (char *)ldrEhdr;
int sz_cto = getLoaderSize() - e_entry;
char *ptr_cto = (char *)const_cast<unsigned char *>(getLoader());
int sz_cto = getLoaderSize();
if (0x20==(ft->id & 0xF0) || 0x30==(ft->id & 0xF0)) { // push byte '?' ; cto8
patch_le16(ptr_cto, sz_cto, "\x6a?", 0x6a + (ft->cto << 8));
}
// PackHeader and overlay_offset at the end of the output file,
// after the compressed data.
// FIXME ?? patchVersion((char *)ldrEhdr, e_entry);
return getLoaderSize();
}
@@ -247,12 +447,12 @@ PackLinuxI386::buildLoader(Filter const *ft)
// filter
optimizeFilter(&fold_ft, buf, sz_fold);
bool success = fold_ft.filter(buf + sizeof(cprElfhdr), sz_fold - sizeof(cprElfhdr));
bool success = fold_ft.filter(buf + sizeof(cprElfHdr2), sz_fold - sizeof(cprElfHdr2));
(void)success;
return buildLinuxLoader(
linux_i386exec_loader, sizeof(linux_i386exec_loader),
buf, sz_fold, ft, 0 );
buf, sz_fold, ft );
}
int PackLinuxI386::getLoaderPrefixSize() const
@@ -366,15 +566,14 @@ bool PackLinuxI386::canPack()
}
void PackLinuxI386::patchLoader()
{
}
void PackLinuxI386::patchLoader() { }
void PackLinuxI386::patchLoaderChecksum()
{
unsigned char *const ptr = const_cast<unsigned char *>(getLoader());
l_info *const lp = (l_info *)(ptr + getLoaderPrefixSize());
l_info *const lp = (l_info *)(sizeof(elfout.ehdr) +
(elfout.ehdr.e_phnum * elfout.ehdr.e_phentsize) + (char *)&elfout );
// checksum for loader + p_info
lp->l_checksum = 0;
lp->l_magic = UPX_ELF_MAGIC;
@@ -383,36 +582,14 @@ void PackLinuxI386::patchLoaderChecksum()
lp->l_format = (unsigned char) ph.format;
// INFO: lp->l_checksum is currently unused
unsigned adler = upx_adler32(0,NULL,0);
adler = upx_adler32(adler, ptr, lsize + sizeof(p_info));
adler = upx_adler32(adler, ptr, lsize);
lp->l_checksum = adler;
}
void PackLinuxI386::updateLoader(OutputFile *fo)
{
#define PAGE_MASK (~0<<12)
Elf_LE32_Ehdr *const ehdr = (Elf_LE32_Ehdr *)const_cast<upx_byte *>(getLoader());
Elf_LE32_Phdr *const phdr = (Elf_LE32_Phdr *)(1+ehdr);
ehdr->e_phnum = 2;
phdr->p_filesz = lsize;
// phdr->p_memsz is the decompressed size
assert(lsize > 128 && lsize < 4096);
// The first Phdr maps the stub (instructions, data, bss) rwx.
// The second Phdr maps the overlay r--,
// to defend against /usr/bin/strip removing the overlay.
Elf_LE32_Phdr *const phdro = 1+phdr;
phdro->p_type = PT_LOAD;
phdro->p_offset = lsize;
phdro->p_paddr = phdro->p_vaddr = 0x00400000 + (lsize &~ PAGE_MASK);
phdro->p_memsz = phdro->p_filesz = fo->getBytesWritten() - lsize;
phdro->p_flags = phdro->PF_R;
phdro->p_align = (unsigned) (-PAGE_MASK);
patchLoaderChecksum();
#undef PAGE_MASK
elfout.ehdr.e_entry = fo->getBytesWritten() + elfout.phdr[0].p_vaddr;
}