attempt to rebuild PE structure from UPX compressed files

git-svn: trunk@1155
remotes/push_mirror/metadata
Tomasz Kojm 21 years ago
parent 93899a2c35
commit 4a24fe30c5
  1. 5
      clamav-devel/ChangeLog
  2. 78
      clamav-devel/libclamav/pe.c
  3. 167
      clamav-devel/libclamav/upx.c
  4. 8
      clamav-devel/libclamav/upx.h

@ -1,3 +1,8 @@
Fri Dec 3 03:32:17 CET 2004 (tk)
---------------------------------
* libclamav: attempt to rebuild PE structure from UPX compressed files
(code from aCaB)
Thu Dec 2 11:10:31 GMT 2004 (njh)
----------------------------------
* clamav-milter: --internal now notices when the database has been

@ -154,7 +154,7 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
struct stat sb;
char sname[9], buff[256], *tempfile;
int i, found, upx_success = 0, min = 0, max = 0, ret;
int (*upxfn)(char *, int , char *, int) = NULL;
int (*upxfn)(char *, int , char *, int *, uint32_t, uint32_t, uint32_t) = NULL;
char *src = NULL, *dest = NULL;
int ssize = -1, dsize = -1, ndesc;
@ -717,6 +717,7 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
fsync(ndesc);
lseek(ndesc, 0, SEEK_SET);
cli_dbgmsg("***** Scanning rebuilt PE file *****\n");
if(cli_magic_scandesc(ndesc, virname, scanned, root, limits, options, arec, mrec) == CL_VIRUS) {
free(section_hdr);
close(ndesc);
@ -909,6 +910,7 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
fsync(ndesc);
lseek(ndesc, 0, SEEK_SET);
cli_dbgmsg("***** Scanning rebuilt PE file *****\n");
if(cli_magic_scandesc(ndesc, virname, scanned, root, limits, options, arec, mrec) == CL_VIRUS) {
free(section_hdr);
close(ndesc);
@ -987,7 +989,7 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
return CL_EMEM;
}
if((dest = (char *) cli_calloc(dsize, sizeof(char))) == NULL) {
if((dest = (char *) cli_calloc(dsize + 1024 + nsections * 40, sizeof(char))) == NULL) {
free(section_hdr);
free(src);
return CL_EMEM;
@ -1037,12 +1039,12 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
if(buff[1] != '\xbe' || skew <= 0 || skew > 0xfff ) { /* FIXME: legit skews?? */
skew = 0;
if(!upxfn(src, ssize, dest, dsize))
if(upxfn(src, ssize, dest, &dsize, EC32(section_hdr[i].VirtualAddress), EC32(section_hdr[i + 1].VirtualAddress), EC32(optional_hdr.AddressOfEntryPoint)) >= 0)
upx_success = 1;
} else {
cli_dbgmsg("UPX: UPX1 seems skewed by %d bytes\n", skew);
if(!upxfn(src + skew, ssize - skew, dest, dsize) || !upxfn(src, ssize, dest, dsize))
if(upxfn(src + skew, ssize - skew, dest, &dsize, EC32(section_hdr[i].VirtualAddress), EC32(section_hdr[i + 1].VirtualAddress), EC32(optional_hdr.AddressOfEntryPoint)-skew) >= 0 || upxfn(src, ssize, dest, &dsize, EC32(section_hdr[i].VirtualAddress), EC32(section_hdr[i + 1].VirtualAddress), EC32(optional_hdr.AddressOfEntryPoint)) >= 0)
upx_success = 1;
}
@ -1053,7 +1055,8 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
}
if(!upx_success && upxfn != upx_inflate2b) {
if(upx_inflate2b(src, ssize, dest, dsize) && upx_inflate2b(src + 0x15, ssize - 0x15, dest, dsize) ) {
if(upx_inflate2b(src, ssize, dest, &dsize, EC32(section_hdr[i].VirtualAddress), EC32(section_hdr[i + 1].VirtualAddress), EC32(optional_hdr.AddressOfEntryPoint)) == -1 && upx_inflate2b(src + 0x15, ssize - 0x15, dest, &dsize, EC32(section_hdr[i].VirtualAddress), EC32(section_hdr[i + 1].VirtualAddress), EC32(optional_hdr.AddressOfEntryPoint) - 0x15) == -1) {
cli_dbgmsg("UPX: NRV2B decompressor failed\n");
} else {
upx_success = 1;
@ -1062,7 +1065,8 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
}
if(!upx_success && upxfn != upx_inflate2d) {
if(upx_inflate2d(src, ssize, dest, dsize) && upx_inflate2d(src + 0x15, ssize - 0x15, dest, dsize) ) {
if(upx_inflate2d(src, ssize, dest, &dsize, EC32(section_hdr[i].VirtualAddress), EC32(section_hdr[i + 1].VirtualAddress), EC32(optional_hdr.AddressOfEntryPoint)) == -1 && upx_inflate2d(src + 0x15, ssize - 0x15, dest, &dsize, EC32(section_hdr[i].VirtualAddress), EC32(section_hdr[i + 1].VirtualAddress), EC32(optional_hdr.AddressOfEntryPoint) - 0x15) == -1) {
cli_dbgmsg("UPX: NRV2D decompressor failed\n");
} else {
upx_success = 1;
@ -1071,7 +1075,7 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
}
if(!upx_success && upxfn != upx_inflate2e) {
if(upx_inflate2e(src, ssize, dest, dsize) && upx_inflate2e(src + 0x15, ssize - 0x15, dest, dsize) ) {
if(upx_inflate2e(src, ssize, dest, &dsize, EC32(section_hdr[i].VirtualAddress), EC32(section_hdr[i + 1].VirtualAddress), EC32(optional_hdr.AddressOfEntryPoint)) == -1 && upx_inflate2e(src + 0x15, ssize - 0x15, dest, &dsize, EC32(section_hdr[i].VirtualAddress), EC32(section_hdr[i + 1].VirtualAddress), EC32(optional_hdr.AddressOfEntryPoint) - 0x15) == -1) {
cli_dbgmsg("UPX: NRV2E decompressor failed\n");
} else {
upx_success = 1;
@ -1087,40 +1091,45 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
}
if(upx_success) {
int ndesc;
if(cli_leavetemps_flag) {
tempfile = cli_gentemp(NULL);
if((ndesc = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU)) < 0) {
cli_dbgmsg("UPX/FSG: Can't create file %s\n", tempfile);
free(tempfile);
free(section_hdr);
free(src);
free(dest);
return CL_EIO;
}
free(src);
free(section_hdr);
if(write(ndesc, dest, dsize) != dsize) {
cli_dbgmsg("UPX/FSG: Can't write %d bytes\n", dsize);
free(tempfile);
free(section_hdr);
free(src);
free(dest);
return CL_EIO;
}
tempfile = cli_gentemp(NULL);
if((ndesc = open(tempfile, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU)) < 0) {
cli_dbgmsg("UPX/FSG: Can't create file %s\n", tempfile);
free(tempfile);
free(dest);
return CL_EIO;
}
if(write(ndesc, dest, dsize) != dsize) {
cli_dbgmsg("UPX/FSG: Can't write %d bytes\n", dsize);
free(tempfile);
free(dest);
close(ndesc);
return CL_EIO;
}
free(dest);
fsync(ndesc);
lseek(ndesc, 0, SEEK_SET);
if(cli_leavetemps_flag)
cli_dbgmsg("UPX/FSG: Decompressed data saved in %s\n", tempfile);
cli_dbgmsg("***** Scanning rebuilt PE file *****\n");
if((ret = cli_magic_scandesc(ndesc, virname, scanned, root, limits, options, arec, mrec)) == CL_VIRUS) {
close(ndesc);
if(!cli_leavetemps_flag)
unlink(tempfile);
free(tempfile);
return CL_VIRUS;
}
if(scanned)
*scanned += dsize / CL_COUNT_PRECISION;
ret = cl_scanbuff(dest, dsize, virname, root);
free(section_hdr);
free(src);
free(dest);
close(ndesc);
if(!cli_leavetemps_flag)
unlink(tempfile);
free(tempfile);
return ret;
}
}
@ -1192,6 +1201,7 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
EC32(optional_hdr.DataDirectory[2].Size))) {
case 1:
cli_dbgmsg("Petite: Unpacked and rebuilt executable saved in %s\n", tempfile);
cli_dbgmsg("***** Scanning rebuilt PE file *****\n");
break;
case 0:

@ -23,6 +23,7 @@
** 08/05/2k4 - Now works as a charm :D
** 09/05/2k4 - Moved code outta main(), got rid of globals for thread safety, added bound checking, minor cleaning
** 04/06/2k4 - Now we handle 2B, 2D and 2E :D
** 28/08/2k4 - PE rebuild for nested packers
*/
/*
@ -48,6 +49,131 @@
#include <string.h>
#include "cltypes.h"
#include "others.h"
#define HEADERS "\
\x4D\x5A\x90\x00\x02\x00\x00\x00\x04\x00\x0F\x00\xFF\xFF\x00\x00\
\xB0\x00\x00\x00\x00\x00\x00\x00\x40\x00\x1A\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xD0\x00\x00\x00\
\x0E\x1F\xB4\x09\xBA\x0D\x00\xCD\x21\xB4\x4C\xCD\x21\x54\x68\x69\
\x73\x20\x66\x69\x6C\x65\x20\x77\x61\x73\x20\x63\x72\x65\x61\x74\
\x65\x64\x20\x62\x79\x20\x43\x6C\x61\x6D\x41\x56\x20\x66\x6F\x72\
\x20\x69\x6E\x74\x65\x72\x6E\x61\x6C\x20\x75\x73\x65\x20\x61\x6E\
\x64\x20\x73\x68\x6F\x75\x6C\x64\x20\x6E\x6F\x74\x20\x62\x65\x20\
\x72\x75\x6E\x2E\x0D\x0A\x43\x6C\x61\x6D\x41\x56\x20\x2D\x20\x41\
\x20\x47\x50\x4C\x20\x76\x69\x72\x75\x73\x20\x73\x63\x61\x6E\x6E\
\x65\x72\x20\x2D\x20\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\
\x63\x6C\x61\x6D\x61\x76\x2E\x6E\x65\x74\x0D\x0A\x24\x00\x00\x00\
"
#if WORDS_BIGENDIAN == 0
#define EC32(v) (v)
#else
static inline uint32_t EC32(uint32_t v)
{
return ((v >> 24) | ((v & 0x00FF0000) >> 8) | ((v & 0x0000FF00) << 8) | (v << 24));
}
#endif
#define cli_writeint32(offset,value) *(uint32_t *)(offset) = EC32(value)
/* PE from UPX */
int pefromupx (char *src, char *dst, int *dsize, uint32_t ep, uint32_t upx0, uint32_t upx1, uint32_t magic)
{
char *imports, *sections, *pehdr, *newbuf;
int sectcnt, upd=1, realstuffsz, align;
int foffset=0xd0+0xf8;
imports = dst + cli_readint32(src + ep - upx1 + magic);
realstuffsz = imports-dst;
if ( realstuffsz < 0 || realstuffsz > *dsize )
return 0;
pehdr = imports;
while (pehdr+7 < dst+*dsize && cli_readint32(pehdr)) {
pehdr+=8;
while(pehdr+1 < dst+*dsize && *pehdr) {
pehdr++;
while (pehdr+1 < dst+*dsize && *pehdr)
pehdr++;
pehdr++;
}
pehdr++;
}
pehdr+=4;
if (pehdr+0xf8 > dst+*dsize)
return 0;
if ( cli_readint32(pehdr) != 0x4550 )
return 0;
if (! (align = cli_readint32(pehdr+0x38)))
return 0;
sections = pehdr+0xf8;
if ( ! (sectcnt = pehdr[6]+256*pehdr[7]))
return 0;
foffset+=0x28*sectcnt;
if (pehdr + 0xf8 + 0x28*sectcnt >= dst + *dsize)
return 0;
for (upd = 0; upd <sectcnt ; upd++) {
uint32_t vsize=cli_readint32(sections+8)-1;
uint32_t rsize=cli_readint32(sections+16);
uint32_t urva=cli_readint32(sections+12);
vsize=(((vsize/0x1000)+1)*0x1000); /* FIXME: get bounds from header */
/* Within bounds ? */
if ( urva < upx0 || urva + vsize > upx0 + realstuffsz)
return 0;
/* Rsize -gt Vsize ? */
if ( rsize > vsize )
return 0;
/* Am i been fooled? There are better ways ;) */
if ( rsize+4 < vsize && cli_readint32(dst+urva-upx0+rsize) )
return 0;
cli_writeint32(sections+8, vsize);
cli_writeint32(sections+20, foffset);
foffset+=rsize;
sections+=0x28;
}
cli_writeint32(pehdr+8, 0x4d414c43);
if (!(newbuf = (char *) cli_malloc(foffset)))
return 0;
memcpy(newbuf, HEADERS, 0xd0);
memcpy(newbuf+0xd0, pehdr,0xf8+0x28*sectcnt);
sections = pehdr+0xf8;
for (upd = 0; upd <sectcnt ; upd++) {
memcpy(newbuf+cli_readint32(sections+20), dst+cli_readint32(sections+12)-upx0, cli_readint32(sections+16));
sections+=0x28;
}
/* CBA restoring the imports they'll look different from the originals anyway... */
/* ...and yeap i miss the icon too :P */
memcpy(dst, newbuf, foffset);
*dsize = foffset;
free(newbuf);
cli_dbgmsg("UPX: PE structure rebuilt from compressed file\n");
return 1;
}
/* [doubleebx] */
@ -82,14 +208,14 @@ static int doubleebx(char *src, int32_t *myebx, int *scur, int ssize)
/* [inflate] */
int upx_inflate2b(char *src, int ssize, char *dst, int dsize)
int upx_inflate2b(char *src, int ssize, char *dst, int *dsize, uint32_t upx0, uint32_t upx1, uint32_t ep)
{
int32_t backbytes, unp_offset = -1, myebx = 0;
int scur=0, dcur=0, i, backsize,oob;
while (1) {
while ((oob = doubleebx(src, &myebx, &scur, ssize)) == 1) {
if (scur<0 || scur>=ssize || dcur<0 || dcur>=dsize)
if (scur<0 || scur>=ssize || dcur<0 || dcur>=*dsize)
return -1;
dst[dcur++] = src[scur++];
}
@ -149,23 +275,31 @@ int upx_inflate2b(char *src, int ssize, char *dst, int dsize)
backsize++;
for (i = 0; i < backsize; i++) {
if (dcur+i<0 || dcur+i>=dsize || dcur+unp_offset+i<0 || dcur+unp_offset+i>=dsize)
if (dcur+i<0 || dcur+i>=*dsize || dcur+unp_offset+i<0 || dcur+unp_offset+i>=*dsize)
return -1;
dst[dcur + i] = dst[dcur + unp_offset + i];
}
dcur+=backsize;
}
if ( ep - upx1 + 0x14f <= ssize-5 && /* Wondering how we got so far?! */
src[ep - upx1 + 0x14f] == '\xe9' && /* JMP OldEip */
src[ep - upx1 + 0x106] == '\x8d' && /* lea edi, ... */
src[ep - upx1 + 0x107] == '\xbe' ) /* ... [esi + offset] */
return pefromupx (src, dst, dsize, ep, upx0, upx1, 0x108);
return 0;
}
int upx_inflate2d(char *src, int ssize, char *dst, int dsize)
int upx_inflate2d(char *src, int ssize, char *dst, int *dsize, uint32_t upx0, uint32_t upx1, uint32_t ep)
{
int32_t backbytes, unp_offset = -1, myebx = 0;
int scur=0, dcur=0, i, backsize, oob;
while (1) {
while ( (oob = doubleebx(src, &myebx, &scur, ssize)) == 1) {
if (scur<0 || scur>=ssize || dcur<0 || dcur>=dsize)
if (scur<0 || scur>=ssize || dcur<0 || dcur>=*dsize)
return -1;
dst[dcur++] = src[scur++];
}
@ -231,16 +365,23 @@ int upx_inflate2d(char *src, int ssize, char *dst, int dsize)
backsize++;
for (i = 0; i < backsize; i++) {
if (dcur+i<0 || dcur+i>=dsize || dcur+unp_offset+i<0 || dcur+unp_offset+i>=dsize)
if (dcur+i<0 || dcur+i>=*dsize || dcur+unp_offset+i<0 || dcur+unp_offset+i>=*dsize)
return -1;
dst[dcur + i] = dst[dcur + unp_offset + i];
}
dcur+=backsize;
}
if ( ep - upx1 + 0x139 <= ssize-5 && /* Wondering how we got so far?! */
src[ep - upx1 + 0x139] == '\xe9' && /* JMP OldEip */
src[ep - upx1 + 0xe7] == '\x8d' && /* lea edi, ... */
src[ep - upx1 + 0xe8] == '\xbe' ) /* ... [esi + offset] */
return pefromupx (src, dst, dsize, ep, upx0, upx1, 0xe9);
return 0;
}
int upx_inflate2e(char *src, int ssize, char *dst, int dsize)
int upx_inflate2e(char *src, int ssize, char *dst, int *dsize, uint32_t upx0, uint32_t upx1, uint32_t ep)
{
int32_t backbytes, unp_offset = -1, myebx = 0;
int scur=0, dcur=0, i, backsize, oob;
@ -249,7 +390,7 @@ int upx_inflate2e(char *src, int ssize, char *dst, int dsize)
while ( (oob = doubleebx(src, &myebx, &scur, ssize)) ) {
if (oob == -1)
return -1;
if (scur<0 || scur>=ssize || dcur<0 || dcur>=dsize)
if (scur<0 || scur>=ssize || dcur<0 || dcur>=*dsize)
return -1;
dst[dcur++] = src[scur++];
}
@ -322,12 +463,18 @@ int upx_inflate2e(char *src, int ssize, char *dst, int dsize)
backsize+=2;
for (i = 0; i < backsize; i++) {
if (dcur+i<0 || dcur+i>=dsize || dcur+unp_offset+i<0 || dcur+unp_offset+i>=dsize)
if (dcur+i<0 || dcur+i>=*dsize || dcur+unp_offset+i<0 || dcur+unp_offset+i>=*dsize)
return -1;
dst[dcur + i] = dst[dcur + unp_offset + i];
}
dcur+=backsize;
}
if ( ep - upx1 + 0x145 <= ssize-5 && /* Wondering how we got so far?! */
src[ep - upx1 + 0x145] == '\xe9' && /* JMP OldEip */
src[ep - upx1 + 0xf3] == '\x8d' && /* lea edi, ... */
src[ep - upx1 + 0xf4] == '\xbe' ) /* ... [esi + offset] */
return pefromupx (src, dst, dsize, ep, upx0, upx1, 0xf5);
return 0;
}

@ -19,8 +19,10 @@
#ifndef __UPX_H
#define __UPX_H
int upx_inflate2b(char *, int , char *, int);
int upx_inflate2d(char *, int , char *, int);
int upx_inflate2e(char *, int , char *, int);
#include "cltypes.h"
int upx_inflate2b(char *, int, char *, int *, uint32_t, uint32_t, uint32_t);
int upx_inflate2d(char *, int, char *, int *, uint32_t, uint32_t, uint32_t);
int upx_inflate2e(char *, int, char *, int *, uint32_t, uint32_t, uint32_t);
#endif

Loading…
Cancel
Save