2022-10-22 18:41:00 +08:00
/*
* Copyright ( C ) 2013 - 2022 Cisco Systems , Inc . and / or its affiliates . All rights reserved .
* Copyright ( C ) 2007 - 2013 Sourcefire , Inc .
*
* Authors : Alberto Wu , Tomasz Kojm , Andrew Williams
*
* Acknowledgements : The header structures were based upon a PE format
* analysis by B . Luevelsmeyer .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
*/
/*
Portions of Code ( i . e . pe_ordinal ) Copyright ( c ) 2014. The YARA Authors . All Rights Reserved .
Licensed under the Apache License , Version 2.0 ( the " License " ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an " AS IS " BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
*/
# if HAVE_CONFIG_H
# include "clamav-config.h"
# endif
/*
# define _XOPEN_SOURCE 500
*/
# include <stdio.h>
# include <stdlib.h>
# include <stdint.h>
# if HAVE_STRING_H
# include <string.h>
# endif
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
# include <time.h>
# include <stdarg.h>
# include "clamav.h"
# include "others.h"
# include "pe.h"
# include "petite.h"
# include "fsg.h"
# include "spin.h"
# include "upx.h"
# include "yc.h"
# include "aspack.h"
# include "wwunpack.h"
# include "unsp.h"
# include "scanners.h"
# include "str.h"
# include "entconv.h"
# include "execs.h"
# include "mew.h"
# include "upack.h"
# include "matcher.h"
# include "matcher-hash.h"
# include "disasm.h"
# include "special.h"
# include "ishield.h"
# include "asn1.h"
# include "json_api.h"
2023-01-14 18:28:39 +08:00
# include "clamav_rust.h"
2022-10-22 18:41:00 +08:00
# define DCONF ctx->dconf->pe
# define PE_IMAGE_DOS_SIGNATURE 0x5a4d /* MZ */
# define PE_IMAGE_DOS_SIGNATURE_OLD 0x4d5a /* ZM */
# define PE_IMAGE_NT_SIGNATURE 0x00004550
# define PE32_SIGNATURE 0x010b
# define PE32P_SIGNATURE 0x020b
# define OPT_HDR_SIZE_DIFF (sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32))
# define UPX_NRV2B "\x11\xdb\x11\xc9\x01\xdb\x75\x07\x8b\x1e\x83\xee\xfc\x11\xdb\x11\xc9\x11\xc9\x75\x20\x41\x01\xdb"
# define UPX_NRV2D "\x83\xf0\xff\x74\x78\xd1\xf8\x89\xc5\xeb\x0b\x01\xdb\x75\x07\x8b\x1e\x83\xee\xfc\x11\xdb\x11\xc9"
# define UPX_NRV2E "\xeb\x52\x31\xc9\x83\xe8\x03\x72\x11\xc1\xe0\x08\x8a\x06\x46\x83\xf0\xff\x74\x75\xd1\xf8\x89\xc5"
# define UPX_LZMA1_FIRST "\x56\x83\xc3\x04\x53\x50\xc7\x03"
# define UPX_LZMA1_SECOND "\x90\x90\x90\x55\x57\x56\x53\x83"
# define UPX_LZMA0 "\x56\x83\xc3\x04\x53\x50\xc7\x03\x03\x00\x00\x00\x90\x90\x90\x55\x57\x56\x53\x83"
# define UPX_LZMA2 "\x56\x83\xc3\x04\x53\x50\xc7\x03\x03\x00\x02\x00\x90\x90\x90\x90\x90\x55\x57\x56"
# define PE_MAXNAMESIZE 256
# define PE_MAXIMPORTS 1024
# define EC64(x) ((uint64_t)cli_readint64(&(x))) /* Convert little endian to host */
# define EC32(x) ((uint32_t)cli_readint32(&(x)))
# define EC16(x) ((uint16_t)cli_readint16(&(x)))
/* lower and upper boundary alignment (size vs offset) */
# define PEALIGN(o, a) (((a)) ? (((o) / (a)) * (a)) : (o))
# define PESALIGN(o, a) (((a)) ? (((o) / (a) + ((o) % (a) != 0)) * (a)) : (o))
// TODO Replace all of these with static inline functions
# define CLI_UNPSIZELIMITS(NAME, CHK) \
if ( cli_checklimits ( NAME , ctx , ( CHK ) , 0 , 0 ) ! = CL_CLEAN ) { \
cli_exe_info_destroy ( peinfo ) ; \
return CL_CLEAN ; \
}
# define CLI_UNPTEMP(NAME, FREEME) \
if ( ! ( tempfile = cli_gentemp ( ctx - > sub_tmpdir ) ) ) { \
cli_exe_info_destroy ( peinfo ) ; \
cli_multifree FREEME ; \
return CL_EMEM ; \
} \
if ( ( ndesc = open ( tempfile , O_RDWR | O_CREAT | O_TRUNC | O_BINARY , S_IRUSR | S_IWUSR ) ) < 0 ) { \
cli_dbgmsg ( NAME " : Can't create file %s \n " , tempfile ) ; \
free ( tempfile ) ; \
cli_exe_info_destroy ( peinfo ) ; \
cli_multifree FREEME ; \
return CL_ECREAT ; \
}
# define CLI_TMPUNLK() \
if ( ! ctx - > engine - > keeptmp ) { \
if ( cli_unlink ( tempfile ) ) { \
free ( tempfile ) ; \
return CL_EUNLINK ; \
} \
}
# ifdef HAVE__INTERNAL__SHA_COLLECT
# define SHA_OFF \
do { \
ctx - > sha_collect = - 1 ; \
} while ( 0 )
# define SHA_RESET \
do { \
ctx - > sha_collect = sha_collect ; \
} while ( 0 )
# else
# define SHA_OFF \
do { \
} while ( 0 )
# define SHA_RESET \
do { \
} while ( 0 )
# endif
# define FSGCASE(NAME, FREESEC) \
case 0 : /* Unpacked and NOT rebuilt */ \
cli_dbgmsg ( NAME " : Successfully decompressed \n " ) ; \
close ( ndesc ) ; \
if ( cli_unlink ( tempfile ) ) { \
cli_exe_info_destroy ( peinfo ) ; \
free ( tempfile ) ; \
FREESEC ; \
return CL_EUNLINK ; \
} \
free ( tempfile ) ; \
FREESEC ; \
found = 0 ; \
upx_success = 1 ; \
break ; /* FSG ONLY! - scan raw data after upx block */
# define SPINCASE() \
case 2 : \
free ( spinned ) ; \
close ( ndesc ) ; \
if ( cli_unlink ( tempfile ) ) { \
cli_exe_info_destroy ( peinfo ) ; \
free ( tempfile ) ; \
return CL_EUNLINK ; \
} \
cli_dbgmsg ( " cli_scanpe: PESpin: Size exceeded \n " ) ; \
free ( tempfile ) ; \
break ;
2023-01-14 18:28:39 +08:00
# define CLI_UNPRESULTS_(NAME, FSGSTUFF, EXPR, GOOD, FREEME) \
switch ( EXPR ) { \
case GOOD : /* Unpacked and rebuilt */ \
cli_dbgmsg ( NAME " : Unpacked and rebuilt executable saved in %s \n " , tempfile ) ; \
cli_multifree FREEME ; \
cli_exe_info_destroy ( peinfo ) ; \
lseek ( ndesc , 0 , SEEK_SET ) ; \
cli_dbgmsg ( " ***** Scanning rebuilt PE file ***** \n " ) ; \
SHA_OFF ; \
if ( CL_SUCCESS ! = ( ret = cli_magic_scan_desc ( ndesc , tempfile , ctx , NULL , LAYER_ATTRIBUTES_NONE ) ) ) { \
close ( ndesc ) ; \
SHA_RESET ; \
CLI_TMPUNLK ( ) ; \
free ( tempfile ) ; \
return ret ; \
} \
SHA_RESET ; \
close ( ndesc ) ; \
CLI_TMPUNLK ( ) ; \
free ( tempfile ) ; \
return CL_CLEAN ; \
\
FSGSTUFF ; \
\
default : \
cli_dbgmsg ( NAME " : Unpacking failed \n " ) ; \
close ( ndesc ) ; \
if ( cli_unlink ( tempfile ) ) { \
cli_exe_info_destroy ( peinfo ) ; \
free ( tempfile ) ; \
cli_multifree FREEME ; \
return CL_EUNLINK ; \
} \
cli_multifree FREEME ; \
free ( tempfile ) ; \
}
// The GOOD parameter indicates what a successful unpacking should return.
2022-10-22 18:41:00 +08:00
# define CLI_UNPRESULTS(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, (void)0, EXPR, GOOD, FREEME)
2023-01-14 18:28:39 +08:00
2022-10-22 18:41:00 +08:00
// TODO The second argument to FSGCASE below should match what gets freed as
// indicated by FREEME, otherwise a memory leak can occur (as currently used,
// it looks like dest can get leaked by these macros).
# define CLI_UNPRESULTSFSG1(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, FSGCASE(NAME, free(sections)), EXPR, GOOD, FREEME)
# define CLI_UNPRESULTSFSG2(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, FSGCASE(NAME, (void)0), EXPR, GOOD, FREEME)
# define DETECT_BROKEN_PE (SCAN_HEURISTIC_BROKEN && !ctx->corrupted_input)
extern const unsigned int hashlen [ ] ;
struct offset_list {
uint32_t offset ;
struct offset_list * next ;
} ;
struct pe_image_import_descriptor {
union {
uint32_t Characteristics ;
uint32_t OriginalFirstThunk ;
} u ;
uint32_t TimeDateStamp ;
uint32_t ForwarderChain ;
uint32_t Name ;
uint32_t FirstThunk ;
} ;
# define PE_IMAGEDIR_ORDINAL_FLAG32 0x80000000
# define PE_IMAGEDIR_ORDINAL_FLAG64 0x8000000000000000L
struct pe_image_thunk32 {
union {
uint32_t ForwarderString ;
uint32_t Function ;
uint32_t Ordinal ;
uint32_t AddressOfData ;
} u ;
} ;
struct pe_image_thunk64 {
union {
uint64_t ForwarderString ;
uint64_t Function ;
uint64_t Ordinal ;
uint64_t AddressOfData ;
} u ;
} ;
struct pe_image_import_by_name {
uint16_t Hint ;
uint8_t Name [ 1 ] ;
} ;
static void cli_multifree ( void * f , . . . )
{
void * ff ;
va_list ap ;
free ( f ) ;
va_start ( ap , f ) ;
while ( ( ff = va_arg ( ap , void * ) ) ) free ( ff ) ;
va_end ( ap ) ;
}
struct vinfo_list {
uint32_t rvas [ 16 ] ;
unsigned int count ;
} ;
static int versioninfo_cb ( void * opaque , uint32_t type , uint32_t name , uint32_t lang , uint32_t rva )
{
struct vinfo_list * vlist = ( struct vinfo_list * ) opaque ;
cli_dbgmsg ( " versioninfo_cb: type: %x, name: %x, lang: %x, rva: %x \n " , type , name , lang , rva ) ;
vlist - > rvas [ vlist - > count ] = rva ;
if ( + + vlist - > count = = sizeof ( vlist - > rvas ) / sizeof ( vlist - > rvas [ 0 ] ) )
return 1 ;
return 0 ;
}
/* Given an RVA (relative to the ImageBase), return the file offset of the
* corresponding data */
uint32_t cli_rawaddr ( uint32_t rva , const struct cli_exe_section * shp , uint16_t nos , unsigned int * err , size_t fsize , uint32_t hdr_size )
{
int i , found = 0 ;
uint32_t ret ;
if ( rva < hdr_size ) { /* Out of section EP - mapped to imagebase+rva */
if ( rva > = fsize ) {
* err = 1 ;
return 0 ;
}
* err = 0 ;
return rva ;
}
for ( i = nos - 1 ; i > = 0 ; i - - ) {
if ( shp [ i ] . rsz & & shp [ i ] . rva < = rva & & shp [ i ] . rsz > ( rva - shp [ i ] . rva ) ) {
found = 1 ;
break ;
}
}
if ( ! found ) {
* err = 1 ;
return 0 ;
}
ret = ( rva - shp [ i ] . rva ) + shp [ i ] . raw ;
* err = 0 ;
return ret ;
}
/*
void findres ( uint32_t by_type , uint32_t by_name , fmap_t * map , struct cli_exe_info * peinfo , int ( * cb ) ( void * , uint32_t , uint32_t , uint32_t , uint32_t ) , void * opaque )
callback based res lookup
by_type : lookup type
by_name : lookup name or ( unsigned ) - 1 to look for any name
res_rva : base resource rva ( i . e . dirs [ 2 ] . VirtualAddress )
map , peinfo : same as in scanpe
cb : the callback function executed on each successful match
opaque : an opaque pointer passed to the callback
the callback proto is
int pe_res_cballback ( void * opaque , uint32_t type , uint32_t name , uint32_t lang , uint32_t rva ) ;
the callback shall return 0 to continue the lookup or 1 to abort
*/
void findres ( uint32_t by_type , uint32_t by_name , fmap_t * map , struct cli_exe_info * peinfo , int ( * cb ) ( void * , uint32_t , uint32_t , uint32_t , uint32_t ) , void * opaque )
{
unsigned int err = 0 ;
uint32_t type , type_offs , name , name_offs , lang , lang_offs ;
const uint8_t * resdir , * type_entry , * name_entry , * lang_entry ;
uint16_t type_cnt , name_cnt , lang_cnt ;
uint32_t res_rva ;
if ( NULL = = peinfo | | peinfo - > ndatadirs < 3 ) {
return ;
}
if ( 0 ! = peinfo - > offset ) {
cli_dbgmsg ( " findres: Assumption Violated: Looking for version info when peinfo->offset != 0 \n " ) ;
}
res_rva = EC32 ( peinfo - > dirs [ 2 ] . VirtualAddress ) ;
if ( ! ( resdir = fmap_need_off_once ( map , cli_rawaddr ( res_rva , peinfo - > sections , peinfo - > nsections , & err , map - > len , peinfo - > hdr_size ) , 16 ) ) | | err )
return ;
type_cnt = ( uint16_t ) cli_readint16 ( resdir + 12 ) ;
type_entry = resdir + 16 ;
if ( ! ( by_type > > 31 ) ) {
type_entry + = type_cnt * 8 ;
type_cnt = ( uint16_t ) cli_readint16 ( resdir + 14 ) ;
}
while ( type_cnt - - ) {
if ( ! fmap_need_ptr_once ( map , type_entry , 8 ) )
return ;
type = cli_readint32 ( type_entry ) ;
type_offs = cli_readint32 ( type_entry + 4 ) ;
if ( type = = by_type & & ( type_offs > > 31 ) ) {
type_offs & = 0x7fffffff ;
if ( ! ( resdir = fmap_need_off_once ( map , cli_rawaddr ( res_rva + type_offs , peinfo - > sections , peinfo - > nsections , & err , map - > len , peinfo - > hdr_size ) , 16 ) ) | | err )
return ;
name_cnt = ( uint16_t ) cli_readint16 ( resdir + 12 ) ;
name_entry = resdir + 16 ;
if ( by_name = = 0xffffffff )
name_cnt + = ( uint16_t ) cli_readint16 ( resdir + 14 ) ;
else if ( ! ( by_name > > 31 ) ) {
name_entry + = name_cnt * 8 ;
name_cnt = ( uint16_t ) cli_readint16 ( resdir + 14 ) ;
}
while ( name_cnt - - ) {
if ( ! fmap_need_ptr_once ( map , name_entry , 8 ) )
return ;
name = cli_readint32 ( name_entry ) ;
name_offs = cli_readint32 ( name_entry + 4 ) ;
if ( ( by_name = = 0xffffffff | | name = = by_name ) & & ( name_offs > > 31 ) ) {
name_offs & = 0x7fffffff ;
if ( ! ( resdir = fmap_need_off_once ( map , cli_rawaddr ( res_rva + name_offs , peinfo - > sections , peinfo - > nsections , & err , map - > len , peinfo - > hdr_size ) , 16 ) ) | | err )
return ;
lang_cnt = ( uint16_t ) cli_readint16 ( resdir + 12 ) + ( uint16_t ) cli_readint16 ( resdir + 14 ) ;
lang_entry = resdir + 16 ;
while ( lang_cnt - - ) {
if ( ! fmap_need_ptr_once ( map , lang_entry , 8 ) )
return ;
lang = cli_readint32 ( lang_entry ) ;
lang_offs = cli_readint32 ( lang_entry + 4 ) ;
if ( ! ( lang_offs > > 31 ) ) {
if ( cb ( opaque , type , name , lang , res_rva + lang_offs ) )
return ;
}
lang_entry + = 8 ;
}
}
name_entry + = 8 ;
}
return ; /* FIXME: unless we want to find ALL types */
}
type_entry + = 8 ;
}
}
static void cli_parseres_special ( uint32_t base , uint32_t rva , fmap_t * map , struct cli_exe_info * peinfo , size_t fsize , unsigned int level , uint32_t type , unsigned int * maxres , struct swizz_stats * stats )
{
unsigned int err = 0 , i ;
const uint8_t * resdir ;
const uint8_t * entry , * oentry ;
uint16_t named , unnamed ;
uint32_t rawaddr = cli_rawaddr ( rva , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
uint32_t entries ;
if ( level > 2 | | ! * maxres ) return ;
* maxres - = 1 ;
if ( err | | ! ( resdir = fmap_need_off_once ( map , rawaddr , 16 ) ) )
return ;
named = ( uint16_t ) cli_readint16 ( resdir + 12 ) ;
unnamed = ( uint16_t ) cli_readint16 ( resdir + 14 ) ;
entries = /*named+*/ unnamed ;
if ( ! entries )
return ;
rawaddr + = named * 8 ; /* skip named */
/* this is just used in a heuristic detection, so don't give error on failure */
if ( ! ( entry = fmap_need_off ( map , rawaddr + 16 , entries * 8 ) ) ) {
cli_dbgmsg ( " cli_parseres_special: failed to read resource directory at:%lu \n " , ( unsigned long ) rawaddr + 16 ) ;
return ;
}
oentry = entry ;
/*for (i=0; i<named; i++) {
uint32_t id , offs ;
id = cli_readint32 ( entry ) ;
offs = cli_readint32 ( entry + 4 ) ;
if ( offs > > 31 )
cli_parseres ( base , base + ( offs & 0x7fffffff ) , srcfd , peinfo , fsize , level + 1 , type , maxres , stats ) ;
entry + = 8 ;
} */
for ( i = 0 ; i < unnamed ; i + + , entry + = 8 ) {
uint32_t id , offs ;
if ( stats - > errors > = SWIZZ_MAXERRORS ) {
cli_dbgmsg ( " cli_parseres_special: resources broken, ignoring \n " ) ;
return ;
}
id = cli_readint32 ( entry ) & 0x7fffffff ;
if ( level = = 0 ) {
type = 0 ;
switch ( id ) {
case 4 : /* menu */
case 5 : /* dialog */
case 6 : /* string */
case 11 : /* msgtable */
type = id ;
break ;
case 16 :
type = id ;
/* 14: version */
stats - > has_version = 1 ;
break ;
case 24 : /* manifest */
stats - > has_manifest = 1 ;
break ;
/* otherwise keep it 0, we don't want it */
}
}
if ( ! type ) {
/* if we are not interested in this type, skip */
continue ;
}
offs = cli_readint32 ( entry + 4 ) ;
if ( offs > > 31 )
cli_parseres_special ( base , base + ( offs & 0x7fffffff ) , map , peinfo , fsize , level + 1 , type , maxres , stats ) ;
else {
offs = cli_readint32 ( entry + 4 ) ;
rawaddr = cli_rawaddr ( base + offs , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( ! err & & ( resdir = fmap_need_off_once ( map , rawaddr , 16 ) ) ) {
uint32_t isz = cli_readint32 ( resdir + 4 ) ;
const uint8_t * str ;
rawaddr = cli_rawaddr ( cli_readint32 ( resdir ) , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( err | | ! isz | | isz > = fsize | | rawaddr + isz > = fsize ) {
cli_dbgmsg ( " cli_parseres_special: invalid resource table entry: %lu + %lu \n " ,
( unsigned long ) rawaddr ,
( unsigned long ) isz ) ;
stats - > errors + + ;
continue ;
}
if ( ( id & 0xff ) ! = 0x09 ) /* english res only */
continue ;
if ( ( str = fmap_need_off_once ( map , rawaddr , isz ) ) )
cli_detect_swizz_str ( str , isz , stats , type ) ;
}
}
}
fmap_unneed_ptr ( map , oentry , entries * 8 ) ;
}
static unsigned int cli_hashsect ( fmap_t * map , struct cli_exe_section * s , unsigned char * * digest , int * foundhash , int * foundwild )
{
const void * hashme ;
if ( s - > rsz > CLI_MAX_ALLOCATION ) {
cli_dbgmsg ( " cli_hashsect: skipping hash calculation for too big section \n " ) ;
return 0 ;
}
if ( ! s - > rsz ) return 0 ;
if ( ! ( hashme = fmap_need_off_once ( map , s - > raw , s - > rsz ) ) ) {
cli_dbgmsg ( " cli_hashsect: unable to read section data \n " ) ;
return 0 ;
}
if ( foundhash [ CLI_HASH_MD5 ] | | foundwild [ CLI_HASH_MD5 ] )
cl_hash_data ( " md5 " , hashme , s - > rsz , digest [ CLI_HASH_MD5 ] , NULL ) ;
if ( foundhash [ CLI_HASH_SHA1 ] | | foundwild [ CLI_HASH_SHA1 ] )
cl_sha1 ( hashme , s - > rsz , digest [ CLI_HASH_SHA1 ] , NULL ) ;
if ( foundhash [ CLI_HASH_SHA256 ] | | foundwild [ CLI_HASH_SHA256 ] )
cl_sha256 ( hashme , s - > rsz , digest [ CLI_HASH_SHA256 ] , NULL ) ;
return 1 ;
}
/* check hash section sigs */
2023-01-14 18:28:39 +08:00
static cl_error_t scan_pe_mdb ( cli_ctx * ctx , struct cli_exe_section * exe_section )
2022-10-22 18:41:00 +08:00
{
struct cli_matcher * mdb_sect = ctx - > engine - > hm_mdb ;
unsigned char * hashset [ CLI_HASH_AVAIL_TYPES ] ;
const char * virname = NULL ;
int foundsize [ CLI_HASH_AVAIL_TYPES ] ;
int foundwild [ CLI_HASH_AVAIL_TYPES ] ;
2023-01-14 18:28:39 +08:00
cli_hash_type_t type ;
cl_error_t ret = CL_CLEAN ;
2022-10-22 18:41:00 +08:00
unsigned char * md5 = NULL ;
/* pick hashtypes to generate */
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + ) {
foundsize [ type ] = cli_hm_have_size ( mdb_sect , type , exe_section - > rsz ) ;
foundwild [ type ] = cli_hm_have_wild ( mdb_sect , type ) ;
if ( foundsize [ type ] | | foundwild [ type ] ) {
hashset [ type ] = cli_malloc ( hashlen [ type ] ) ;
if ( ! hashset [ type ] ) {
cli_errmsg ( " scan_pe_mdb: cli_malloc failed! \n " ) ;
for ( ; type > 0 ; )
free ( hashset [ - - type ] ) ;
return CL_EMEM ;
}
} else {
hashset [ type ] = NULL ;
}
}
/* Generate hashes */
cli_hashsect ( ctx - > fmap , exe_section , hashset , foundsize , foundwild ) ;
/* Print hash */
if ( cli_debug_flag ) {
md5 = hashset [ CLI_HASH_MD5 ] ;
if ( md5 ) {
cli_dbgmsg ( " MDB hashset: %u:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x \n " ,
exe_section - > rsz , md5 [ 0 ] , md5 [ 1 ] , md5 [ 2 ] , md5 [ 3 ] , md5 [ 4 ] , md5 [ 5 ] , md5 [ 6 ] , md5 [ 7 ] ,
md5 [ 8 ] , md5 [ 9 ] , md5 [ 10 ] , md5 [ 11 ] , md5 [ 12 ] , md5 [ 13 ] , md5 [ 14 ] , md5 [ 15 ] ) ;
} else if ( cli_always_gen_section_hash ) {
const void * hashme = fmap_need_off_once ( ctx - > fmap , exe_section - > raw , exe_section - > rsz ) ;
if ( ! ( hashme ) ) {
cli_errmsg ( " scan_pe_mdb: unable to read section data \n " ) ;
ret = CL_EREAD ;
goto end ;
}
md5 = cli_malloc ( 16 ) ;
if ( ! ( md5 ) ) {
cli_errmsg ( " scan_pe_mdb: cli_malloc failed! \n " ) ;
ret = CL_EMEM ;
goto end ;
}
cl_hash_data ( " md5 " , hashme , exe_section - > rsz , md5 , NULL ) ;
cli_dbgmsg ( " MDB: %u:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x \n " ,
exe_section - > rsz , md5 [ 0 ] , md5 [ 1 ] , md5 [ 2 ] , md5 [ 3 ] , md5 [ 4 ] , md5 [ 5 ] , md5 [ 6 ] , md5 [ 7 ] ,
md5 [ 8 ] , md5 [ 9 ] , md5 [ 10 ] , md5 [ 11 ] , md5 [ 12 ] , md5 [ 13 ] , md5 [ 14 ] , md5 [ 15 ] ) ;
free ( md5 ) ;
} else {
cli_dbgmsg ( " MDB: %u:notgenerated \n " , exe_section - > rsz ) ;
}
}
/* Do scans */
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + ) {
if ( foundsize [ type ] & & cli_hm_scan ( hashset [ type ] , exe_section - > rsz , & virname , mdb_sect , type ) = = CL_VIRUS ) {
ret = cli_append_virus ( ctx , virname ) ;
2023-01-14 18:28:39 +08:00
if ( ret ! = CL_SUCCESS ) {
break ;
2022-10-22 18:41:00 +08:00
}
}
if ( foundwild [ type ] & & cli_hm_scan_wild ( hashset [ type ] , & virname , mdb_sect , type ) = = CL_VIRUS ) {
ret = cli_append_virus ( ctx , virname ) ;
2023-01-14 18:28:39 +08:00
if ( ret ! = CL_SUCCESS ) {
break ;
2022-10-22 18:41:00 +08:00
}
}
}
end :
for ( type = CLI_HASH_AVAIL_TYPES ; type > 0 ; )
free ( hashset [ - - type ] ) ;
return ret ;
}
/* imptbl scanning */
static char * pe_ordinal ( const char * dll , uint16_t ord )
{
char name [ 64 ] ;
name [ 0 ] = ' \0 ' ;
if ( strncasecmp ( dll , " WS2_32.dll " , 10 ) = = 0 | |
strncasecmp ( dll , " wsock32.dll " , 11 ) = = 0 ) {
switch ( ord ) {
case 1 :
sprintf ( name , " accept " ) ;
break ;
case 2 :
sprintf ( name , " bind " ) ;
break ;
case 3 :
sprintf ( name , " closesocket " ) ;
break ;
case 4 :
sprintf ( name , " connect " ) ;
break ;
case 5 :
sprintf ( name , " getpeername " ) ;
break ;
case 6 :
sprintf ( name , " getsockname " ) ;
break ;
case 7 :
sprintf ( name , " getsockopt " ) ;
break ;
case 8 :
sprintf ( name , " htonl " ) ;
break ;
case 9 :
sprintf ( name , " htons " ) ;
break ;
case 10 :
sprintf ( name , " ioctlsocket " ) ;
break ;
case 11 :
sprintf ( name , " inet_addr " ) ;
break ;
case 12 :
sprintf ( name , " inet_ntoa " ) ;
break ;
case 13 :
sprintf ( name , " listen " ) ;
break ;
case 14 :
sprintf ( name , " ntohl " ) ;
break ;
case 15 :
sprintf ( name , " ntohs " ) ;
break ;
case 16 :
sprintf ( name , " recv " ) ;
break ;
case 17 :
sprintf ( name , " recvfrom " ) ;
break ;
case 18 :
sprintf ( name , " select " ) ;
break ;
case 19 :
sprintf ( name , " send " ) ;
break ;
case 20 :
sprintf ( name , " sendto " ) ;
break ;
case 21 :
sprintf ( name , " setsockopt " ) ;
break ;
case 22 :
sprintf ( name , " shutdown " ) ;
break ;
case 23 :
sprintf ( name , " socket " ) ;
break ;
case 24 :
sprintf ( name , " GetAddrInfoW " ) ;
break ;
case 25 :
sprintf ( name , " GetNameInfoW " ) ;
break ;
case 26 :
sprintf ( name , " WSApSetPostRoutine " ) ;
break ;
case 27 :
sprintf ( name , " FreeAddrInfoW " ) ;
break ;
case 28 :
sprintf ( name , " WPUCompleteOverlappedRequest " ) ;
break ;
case 29 :
sprintf ( name , " WSAAccept " ) ;
break ;
case 30 :
sprintf ( name , " WSAAddressToStringA " ) ;
break ;
case 31 :
sprintf ( name , " WSAAddressToStringW " ) ;
break ;
case 32 :
sprintf ( name , " WSACloseEvent " ) ;
break ;
case 33 :
sprintf ( name , " WSAConnect " ) ;
break ;
case 34 :
sprintf ( name , " WSACreateEvent " ) ;
break ;
case 35 :
sprintf ( name , " WSADuplicateSocketA " ) ;
break ;
case 36 :
sprintf ( name , " WSADuplicateSocketW " ) ;
break ;
case 37 :
sprintf ( name , " WSAEnumNameSpaceProvidersA " ) ;
break ;
case 38 :
sprintf ( name , " WSAEnumNameSpaceProvidersW " ) ;
break ;
case 39 :
sprintf ( name , " WSAEnumNetworkEvents " ) ;
break ;
case 40 :
sprintf ( name , " WSAEnumProtocolsA " ) ;
break ;
case 41 :
sprintf ( name , " WSAEnumProtocolsW " ) ;
break ;
case 42 :
sprintf ( name , " WSAEventSelect " ) ;
break ;
case 43 :
sprintf ( name , " WSAGetOverlappedResult " ) ;
break ;
case 44 :
sprintf ( name , " WSAGetQOSByName " ) ;
break ;
case 45 :
sprintf ( name , " WSAGetServiceClassInfoA " ) ;
break ;
case 46 :
sprintf ( name , " WSAGetServiceClassInfoW " ) ;
break ;
case 47 :
sprintf ( name , " WSAGetServiceClassNameByClassIdA " ) ;
break ;
case 48 :
sprintf ( name , " WSAGetServiceClassNameByClassIdW " ) ;
break ;
case 49 :
sprintf ( name , " WSAHtonl " ) ;
break ;
case 50 :
sprintf ( name , " WSAHtons " ) ;
break ;
case 51 :
sprintf ( name , " gethostbyaddr " ) ;
break ;
case 52 :
sprintf ( name , " gethostbyname " ) ;
break ;
case 53 :
sprintf ( name , " getprotobyname " ) ;
break ;
case 54 :
sprintf ( name , " getprotobynumber " ) ;
break ;
case 55 :
sprintf ( name , " getservbyname " ) ;
break ;
case 56 :
sprintf ( name , " getservbyport " ) ;
break ;
case 57 :
sprintf ( name , " gethostname " ) ;
break ;
case 58 :
sprintf ( name , " WSAInstallServiceClassA " ) ;
break ;
case 59 :
sprintf ( name , " WSAInstallServiceClassW " ) ;
break ;
case 60 :
sprintf ( name , " WSAIoctl " ) ;
break ;
case 61 :
sprintf ( name , " WSAJoinLeaf " ) ;
break ;
case 62 :
sprintf ( name , " WSALookupServiceBeginA " ) ;
break ;
case 63 :
sprintf ( name , " WSALookupServiceBeginW " ) ;
break ;
case 64 :
sprintf ( name , " WSALookupServiceEnd " ) ;
break ;
case 65 :
sprintf ( name , " WSALookupServiceNextA " ) ;
break ;
case 66 :
sprintf ( name , " WSALookupServiceNextW " ) ;
break ;
case 67 :
sprintf ( name , " WSANSPIoctl " ) ;
break ;
case 68 :
sprintf ( name , " WSANtohl " ) ;
break ;
case 69 :
sprintf ( name , " WSANtohs " ) ;
break ;
case 70 :
sprintf ( name , " WSAProviderConfigChange " ) ;
break ;
case 71 :
sprintf ( name , " WSARecv " ) ;
break ;
case 72 :
sprintf ( name , " WSARecvDisconnect " ) ;
break ;
case 73 :
sprintf ( name , " WSARecvFrom " ) ;
break ;
case 74 :
sprintf ( name , " WSARemoveServiceClass " ) ;
break ;
case 75 :
sprintf ( name , " WSAResetEvent " ) ;
break ;
case 76 :
sprintf ( name , " WSASend " ) ;
break ;
case 77 :
sprintf ( name , " WSASendDisconnect " ) ;
break ;
case 78 :
sprintf ( name , " WSASendTo " ) ;
break ;
case 79 :
sprintf ( name , " WSASetEvent " ) ;
break ;
case 80 :
sprintf ( name , " WSASetServiceA " ) ;
break ;
case 81 :
sprintf ( name , " WSASetServiceW " ) ;
break ;
case 82 :
sprintf ( name , " WSASocketA " ) ;
break ;
case 83 :
sprintf ( name , " WSASocketW " ) ;
break ;
case 84 :
sprintf ( name , " WSAStringToAddressA " ) ;
break ;
case 85 :
sprintf ( name , " WSAStringToAddressW " ) ;
break ;
case 86 :
sprintf ( name , " WSAWaitForMultipleEvents " ) ;
break ;
case 87 :
sprintf ( name , " WSCDeinstallProvider " ) ;
break ;
case 88 :
sprintf ( name , " WSCEnableNSProvider " ) ;
break ;
case 89 :
sprintf ( name , " WSCEnumProtocols " ) ;
break ;
case 90 :
sprintf ( name , " WSCGetProviderPath " ) ;
break ;
case 91 :
sprintf ( name , " WSCInstallNameSpace " ) ;
break ;
case 92 :
sprintf ( name , " WSCInstallProvider " ) ;
break ;
case 93 :
sprintf ( name , " WSCUnInstallNameSpace " ) ;
break ;
case 94 :
sprintf ( name , " WSCUpdateProvider " ) ;
break ;
case 95 :
sprintf ( name , " WSCWriteNameSpaceOrder " ) ;
break ;
case 96 :
sprintf ( name , " WSCWriteProviderOrder " ) ;
break ;
case 97 :
sprintf ( name , " freeaddrinfo " ) ;
break ;
case 98 :
sprintf ( name , " getaddrinfo " ) ;
break ;
case 99 :
sprintf ( name , " getnameinfo " ) ;
break ;
case 101 :
sprintf ( name , " WSAAsyncSelect " ) ;
break ;
case 102 :
sprintf ( name , " WSAAsyncGetHostByAddr " ) ;
break ;
case 103 :
sprintf ( name , " WSAAsyncGetHostByName " ) ;
break ;
case 104 :
sprintf ( name , " WSAAsyncGetProtoByNumber " ) ;
break ;
case 105 :
sprintf ( name , " WSAAsyncGetProtoByName " ) ;
break ;
case 106 :
sprintf ( name , " WSAAsyncGetServByPort " ) ;
break ;
case 107 :
sprintf ( name , " WSAAsyncGetServByName " ) ;
break ;
case 108 :
sprintf ( name , " WSACancelAsyncRequest " ) ;
break ;
case 109 :
sprintf ( name , " WSASetBlockingHook " ) ;
break ;
case 110 :
sprintf ( name , " WSAUnhookBlockingHook " ) ;
break ;
case 111 :
sprintf ( name , " WSAGetLastError " ) ;
break ;
case 112 :
sprintf ( name , " WSASetLastError " ) ;
break ;
case 113 :
sprintf ( name , " WSACancelBlockingCall " ) ;
break ;
case 114 :
sprintf ( name , " WSAIsBlocking " ) ;
break ;
case 115 :
sprintf ( name , " WSAStartup " ) ;
break ;
case 116 :
sprintf ( name , " WSACleanup " ) ;
break ;
case 151 :
sprintf ( name , " __WSAFDIsSet " ) ;
break ;
case 500 :
sprintf ( name , " WEP " ) ;
break ;
default :
break ;
}
} else if ( strncasecmp ( dll , " oleaut32.dll " , 12 ) = = 0 ) {
switch ( ord ) {
case 2 :
sprintf ( name , " SysAllocString " ) ;
break ;
case 3 :
sprintf ( name , " SysReAllocString " ) ;
break ;
case 4 :
sprintf ( name , " SysAllocStringLen " ) ;
break ;
case 5 :
sprintf ( name , " SysReAllocStringLen " ) ;
break ;
case 6 :
sprintf ( name , " SysFreeString " ) ;
break ;
case 7 :
sprintf ( name , " SysStringLen " ) ;
break ;
case 8 :
sprintf ( name , " VariantInit " ) ;
break ;
case 9 :
sprintf ( name , " VariantClear " ) ;
break ;
case 10 :
sprintf ( name , " VariantCopy " ) ;
break ;
case 11 :
sprintf ( name , " VariantCopyInd " ) ;
break ;
case 12 :
sprintf ( name , " VariantChangeType " ) ;
break ;
case 13 :
sprintf ( name , " VariantTimeToDosDateTime " ) ;
break ;
case 14 :
sprintf ( name , " DosDateTimeToVariantTime " ) ;
break ;
case 15 :
sprintf ( name , " SafeArrayCreate " ) ;
break ;
case 16 :
sprintf ( name , " SafeArrayDestroy " ) ;
break ;
case 17 :
sprintf ( name , " SafeArrayGetDim " ) ;
break ;
case 18 :
sprintf ( name , " SafeArrayGetElemsize " ) ;
break ;
case 19 :
sprintf ( name , " SafeArrayGetUBound " ) ;
break ;
case 20 :
sprintf ( name , " SafeArrayGetLBound " ) ;
break ;
case 21 :
sprintf ( name , " SafeArrayLock " ) ;
break ;
case 22 :
sprintf ( name , " SafeArrayUnlock " ) ;
break ;
case 23 :
sprintf ( name , " SafeArrayAccessData " ) ;
break ;
case 24 :
sprintf ( name , " SafeArrayUnaccessData " ) ;
break ;
case 25 :
sprintf ( name , " SafeArrayGetElement " ) ;
break ;
case 26 :
sprintf ( name , " SafeArrayPutElement " ) ;
break ;
case 27 :
sprintf ( name , " SafeArrayCopy " ) ;
break ;
case 28 :
sprintf ( name , " DispGetParam " ) ;
break ;
case 29 :
sprintf ( name , " DispGetIDsOfNames " ) ;
break ;
case 30 :
sprintf ( name , " DispInvoke " ) ;
break ;
case 31 :
sprintf ( name , " CreateDispTypeInfo " ) ;
break ;
case 32 :
sprintf ( name , " CreateStdDispatch " ) ;
break ;
case 33 :
sprintf ( name , " RegisterActiveObject " ) ;
break ;
case 34 :
sprintf ( name , " RevokeActiveObject " ) ;
break ;
case 35 :
sprintf ( name , " GetActiveObject " ) ;
break ;
case 36 :
sprintf ( name , " SafeArrayAllocDescriptor " ) ;
break ;
case 37 :
sprintf ( name , " SafeArrayAllocData " ) ;
break ;
case 38 :
sprintf ( name , " SafeArrayDestroyDescriptor " ) ;
break ;
case 39 :
sprintf ( name , " SafeArrayDestroyData " ) ;
break ;
case 40 :
sprintf ( name , " SafeArrayRedim " ) ;
break ;
case 41 :
sprintf ( name , " SafeArrayAllocDescriptorEx " ) ;
break ;
case 42 :
sprintf ( name , " SafeArrayCreateEx " ) ;
break ;
case 43 :
sprintf ( name , " SafeArrayCreateVectorEx " ) ;
break ;
case 44 :
sprintf ( name , " SafeArraySetRecordInfo " ) ;
break ;
case 45 :
sprintf ( name , " SafeArrayGetRecordInfo " ) ;
break ;
case 46 :
sprintf ( name , " VarParseNumFromStr " ) ;
break ;
case 47 :
sprintf ( name , " VarNumFromParseNum " ) ;
break ;
case 48 :
sprintf ( name , " VarI2FromUI1 " ) ;
break ;
case 49 :
sprintf ( name , " VarI2FromI4 " ) ;
break ;
case 50 :
sprintf ( name , " VarI2FromR4 " ) ;
break ;
case 51 :
sprintf ( name , " VarI2FromR8 " ) ;
break ;
case 52 :
sprintf ( name , " VarI2FromCy " ) ;
break ;
case 53 :
sprintf ( name , " VarI2FromDate " ) ;
break ;
case 54 :
sprintf ( name , " VarI2FromStr " ) ;
break ;
case 55 :
sprintf ( name , " VarI2FromDisp " ) ;
break ;
case 56 :
sprintf ( name , " VarI2FromBool " ) ;
break ;
case 57 :
sprintf ( name , " SafeArraySetIID " ) ;
break ;
case 58 :
sprintf ( name , " VarI4FromUI1 " ) ;
break ;
case 59 :
sprintf ( name , " VarI4FromI2 " ) ;
break ;
case 60 :
sprintf ( name , " VarI4FromR4 " ) ;
break ;
case 61 :
sprintf ( name , " VarI4FromR8 " ) ;
break ;
case 62 :
sprintf ( name , " VarI4FromCy " ) ;
break ;
case 63 :
sprintf ( name , " VarI4FromDate " ) ;
break ;
case 64 :
sprintf ( name , " VarI4FromStr " ) ;
break ;
case 65 :
sprintf ( name , " VarI4FromDisp " ) ;
break ;
case 66 :
sprintf ( name , " VarI4FromBool " ) ;
break ;
case 67 :
sprintf ( name , " SafeArrayGetIID " ) ;
break ;
case 68 :
sprintf ( name , " VarR4FromUI1 " ) ;
break ;
case 69 :
sprintf ( name , " VarR4FromI2 " ) ;
break ;
case 70 :
sprintf ( name , " VarR4FromI4 " ) ;
break ;
case 71 :
sprintf ( name , " VarR4FromR8 " ) ;
break ;
case 72 :
sprintf ( name , " VarR4FromCy " ) ;
break ;
case 73 :
sprintf ( name , " VarR4FromDate " ) ;
break ;
case 74 :
sprintf ( name , " VarR4FromStr " ) ;
break ;
case 75 :
sprintf ( name , " VarR4FromDisp " ) ;
break ;
case 76 :
sprintf ( name , " VarR4FromBool " ) ;
break ;
case 77 :
sprintf ( name , " SafeArrayGetVartype " ) ;
break ;
case 78 :
sprintf ( name , " VarR8FromUI1 " ) ;
break ;
case 79 :
sprintf ( name , " VarR8FromI2 " ) ;
break ;
case 80 :
sprintf ( name , " VarR8FromI4 " ) ;
break ;
case 81 :
sprintf ( name , " VarR8FromR4 " ) ;
break ;
case 82 :
sprintf ( name , " VarR8FromCy " ) ;
break ;
case 83 :
sprintf ( name , " VarR8FromDate " ) ;
break ;
case 84 :
sprintf ( name , " VarR8FromStr " ) ;
break ;
case 85 :
sprintf ( name , " VarR8FromDisp " ) ;
break ;
case 86 :
sprintf ( name , " VarR8FromBool " ) ;
break ;
case 87 :
sprintf ( name , " VarFormat " ) ;
break ;
case 88 :
sprintf ( name , " VarDateFromUI1 " ) ;
break ;
case 89 :
sprintf ( name , " VarDateFromI2 " ) ;
break ;
case 90 :
sprintf ( name , " VarDateFromI4 " ) ;
break ;
case 91 :
sprintf ( name , " VarDateFromR4 " ) ;
break ;
case 92 :
sprintf ( name , " VarDateFromR8 " ) ;
break ;
case 93 :
sprintf ( name , " VarDateFromCy " ) ;
break ;
case 94 :
sprintf ( name , " VarDateFromStr " ) ;
break ;
case 95 :
sprintf ( name , " VarDateFromDisp " ) ;
break ;
case 96 :
sprintf ( name , " VarDateFromBool " ) ;
break ;
case 97 :
sprintf ( name , " VarFormatDateTime " ) ;
break ;
case 98 :
sprintf ( name , " VarCyFromUI1 " ) ;
break ;
case 99 :
sprintf ( name , " VarCyFromI2 " ) ;
break ;
case 100 :
sprintf ( name , " VarCyFromI4 " ) ;
break ;
case 101 :
sprintf ( name , " VarCyFromR4 " ) ;
break ;
case 102 :
sprintf ( name , " VarCyFromR8 " ) ;
break ;
case 103 :
sprintf ( name , " VarCyFromDate " ) ;
break ;
case 104 :
sprintf ( name , " VarCyFromStr " ) ;
break ;
case 105 :
sprintf ( name , " VarCyFromDisp " ) ;
break ;
case 106 :
sprintf ( name , " VarCyFromBool " ) ;
break ;
case 107 :
sprintf ( name , " VarFormatNumber " ) ;
break ;
case 108 :
sprintf ( name , " VarBstrFromUI1 " ) ;
break ;
case 109 :
sprintf ( name , " VarBstrFromI2 " ) ;
break ;
case 110 :
sprintf ( name , " VarBstrFromI4 " ) ;
break ;
case 111 :
sprintf ( name , " VarBstrFromR4 " ) ;
break ;
case 112 :
sprintf ( name , " VarBstrFromR8 " ) ;
break ;
case 113 :
sprintf ( name , " VarBstrFromCy " ) ;
break ;
case 114 :
sprintf ( name , " VarBstrFromDate " ) ;
break ;
case 115 :
sprintf ( name , " VarBstrFromDisp " ) ;
break ;
case 116 :
sprintf ( name , " VarBstrFromBool " ) ;
break ;
case 117 :
sprintf ( name , " VarFormatPercent " ) ;
break ;
case 118 :
sprintf ( name , " VarBoolFromUI1 " ) ;
break ;
case 119 :
sprintf ( name , " VarBoolFromI2 " ) ;
break ;
case 120 :
sprintf ( name , " VarBoolFromI4 " ) ;
break ;
case 121 :
sprintf ( name , " VarBoolFromR4 " ) ;
break ;
case 122 :
sprintf ( name , " VarBoolFromR8 " ) ;
break ;
case 123 :
sprintf ( name , " VarBoolFromDate " ) ;
break ;
case 124 :
sprintf ( name , " VarBoolFromCy " ) ;
break ;
case 125 :
sprintf ( name , " VarBoolFromStr " ) ;
break ;
case 126 :
sprintf ( name , " VarBoolFromDisp " ) ;
break ;
case 127 :
sprintf ( name , " VarFormatCurrency " ) ;
break ;
case 128 :
sprintf ( name , " VarWeekdayName " ) ;
break ;
case 129 :
sprintf ( name , " VarMonthName " ) ;
break ;
case 130 :
sprintf ( name , " VarUI1FromI2 " ) ;
break ;
case 131 :
sprintf ( name , " VarUI1FromI4 " ) ;
break ;
case 132 :
sprintf ( name , " VarUI1FromR4 " ) ;
break ;
case 133 :
sprintf ( name , " VarUI1FromR8 " ) ;
break ;
case 134 :
sprintf ( name , " VarUI1FromCy " ) ;
break ;
case 135 :
sprintf ( name , " VarUI1FromDate " ) ;
break ;
case 136 :
sprintf ( name , " VarUI1FromStr " ) ;
break ;
case 137 :
sprintf ( name , " VarUI1FromDisp " ) ;
break ;
case 138 :
sprintf ( name , " VarUI1FromBool " ) ;
break ;
case 139 :
sprintf ( name , " VarFormatFromTokens " ) ;
break ;
case 140 :
sprintf ( name , " VarTokenizeFormatString " ) ;
break ;
case 141 :
sprintf ( name , " VarAdd " ) ;
break ;
case 142 :
sprintf ( name , " VarAnd " ) ;
break ;
case 143 :
sprintf ( name , " VarDiv " ) ;
break ;
case 144 :
sprintf ( name , " DllCanUnloadNow " ) ;
break ;
case 145 :
sprintf ( name , " DllGetClassObject " ) ;
break ;
case 146 :
sprintf ( name , " DispCallFunc " ) ;
break ;
case 147 :
sprintf ( name , " VariantChangeTypeEx " ) ;
break ;
case 148 :
sprintf ( name , " SafeArrayPtrOfIndex " ) ;
break ;
case 149 :
sprintf ( name , " SysStringByteLen " ) ;
break ;
case 150 :
sprintf ( name , " SysAllocStringByteLen " ) ;
break ;
case 151 :
sprintf ( name , " DllRegisterServer " ) ;
break ;
case 152 :
sprintf ( name , " VarEqv " ) ;
break ;
case 153 :
sprintf ( name , " VarIdiv " ) ;
break ;
case 154 :
sprintf ( name , " VarImp " ) ;
break ;
case 155 :
sprintf ( name , " VarMod " ) ;
break ;
case 156 :
sprintf ( name , " VarMul " ) ;
break ;
case 157 :
sprintf ( name , " VarOr " ) ;
break ;
case 158 :
sprintf ( name , " VarPow " ) ;
break ;
case 159 :
sprintf ( name , " VarSub " ) ;
break ;
case 160 :
sprintf ( name , " CreateTypeLib " ) ;
break ;
case 161 :
sprintf ( name , " LoadTypeLib " ) ;
break ;
case 162 :
sprintf ( name , " LoadRegTypeLib " ) ;
break ;
case 163 :
sprintf ( name , " RegisterTypeLib " ) ;
break ;
case 164 :
sprintf ( name , " QueryPathOfRegTypeLib " ) ;
break ;
case 165 :
sprintf ( name , " LHashValOfNameSys " ) ;
break ;
case 166 :
sprintf ( name , " LHashValOfNameSysA " ) ;
break ;
case 167 :
sprintf ( name , " VarXor " ) ;
break ;
case 168 :
sprintf ( name , " VarAbs " ) ;
break ;
case 169 :
sprintf ( name , " VarFix " ) ;
break ;
case 170 :
sprintf ( name , " OaBuildVersion " ) ;
break ;
case 171 :
sprintf ( name , " ClearCustData " ) ;
break ;
case 172 :
sprintf ( name , " VarInt " ) ;
break ;
case 173 :
sprintf ( name , " VarNeg " ) ;
break ;
case 174 :
sprintf ( name , " VarNot " ) ;
break ;
case 175 :
sprintf ( name , " VarRound " ) ;
break ;
case 176 :
sprintf ( name , " VarCmp " ) ;
break ;
case 177 :
sprintf ( name , " VarDecAdd " ) ;
break ;
case 178 :
sprintf ( name , " VarDecDiv " ) ;
break ;
case 179 :
sprintf ( name , " VarDecMul " ) ;
break ;
case 180 :
sprintf ( name , " CreateTypeLib2 " ) ;
break ;
case 181 :
sprintf ( name , " VarDecSub " ) ;
break ;
case 182 :
sprintf ( name , " VarDecAbs " ) ;
break ;
case 183 :
sprintf ( name , " LoadTypeLibEx " ) ;
break ;
case 184 :
sprintf ( name , " SystemTimeToVariantTime " ) ;
break ;
case 185 :
sprintf ( name , " VariantTimeToSystemTime " ) ;
break ;
case 186 :
sprintf ( name , " UnRegisterTypeLib " ) ;
break ;
case 187 :
sprintf ( name , " VarDecFix " ) ;
break ;
case 188 :
sprintf ( name , " VarDecInt " ) ;
break ;
case 189 :
sprintf ( name , " VarDecNeg " ) ;
break ;
case 190 :
sprintf ( name , " VarDecFromUI1 " ) ;
break ;
case 191 :
sprintf ( name , " VarDecFromI2 " ) ;
break ;
case 192 :
sprintf ( name , " VarDecFromI4 " ) ;
break ;
case 193 :
sprintf ( name , " VarDecFromR4 " ) ;
break ;
case 194 :
sprintf ( name , " VarDecFromR8 " ) ;
break ;
case 195 :
sprintf ( name , " VarDecFromDate " ) ;
break ;
case 196 :
sprintf ( name , " VarDecFromCy " ) ;
break ;
case 197 :
sprintf ( name , " VarDecFromStr " ) ;
break ;
case 198 :
sprintf ( name , " VarDecFromDisp " ) ;
break ;
case 199 :
sprintf ( name , " VarDecFromBool " ) ;
break ;
case 200 :
sprintf ( name , " GetErrorInfo " ) ;
break ;
case 201 :
sprintf ( name , " SetErrorInfo " ) ;
break ;
case 202 :
sprintf ( name , " CreateErrorInfo " ) ;
break ;
case 203 :
sprintf ( name , " VarDecRound " ) ;
break ;
case 204 :
sprintf ( name , " VarDecCmp " ) ;
break ;
case 205 :
sprintf ( name , " VarI2FromI1 " ) ;
break ;
case 206 :
sprintf ( name , " VarI2FromUI2 " ) ;
break ;
case 207 :
sprintf ( name , " VarI2FromUI4 " ) ;
break ;
case 208 :
sprintf ( name , " VarI2FromDec " ) ;
break ;
case 209 :
sprintf ( name , " VarI4FromI1 " ) ;
break ;
case 210 :
sprintf ( name , " VarI4FromUI2 " ) ;
break ;
case 211 :
sprintf ( name , " VarI4FromUI4 " ) ;
break ;
case 212 :
sprintf ( name , " VarI4FromDec " ) ;
break ;
case 213 :
sprintf ( name , " VarR4FromI1 " ) ;
break ;
case 214 :
sprintf ( name , " VarR4FromUI2 " ) ;
break ;
case 215 :
sprintf ( name , " VarR4FromUI4 " ) ;
break ;
case 216 :
sprintf ( name , " VarR4FromDec " ) ;
break ;
case 217 :
sprintf ( name , " VarR8FromI1 " ) ;
break ;
case 218 :
sprintf ( name , " VarR8FromUI2 " ) ;
break ;
case 219 :
sprintf ( name , " VarR8FromUI4 " ) ;
break ;
case 220 :
sprintf ( name , " VarR8FromDec " ) ;
break ;
case 221 :
sprintf ( name , " VarDateFromI1 " ) ;
break ;
case 222 :
sprintf ( name , " VarDateFromUI2 " ) ;
break ;
case 223 :
sprintf ( name , " VarDateFromUI4 " ) ;
break ;
case 224 :
sprintf ( name , " VarDateFromDec " ) ;
break ;
case 225 :
sprintf ( name , " VarCyFromI1 " ) ;
break ;
case 226 :
sprintf ( name , " VarCyFromUI2 " ) ;
break ;
case 227 :
sprintf ( name , " VarCyFromUI4 " ) ;
break ;
case 228 :
sprintf ( name , " VarCyFromDec " ) ;
break ;
case 229 :
sprintf ( name , " VarBstrFromI1 " ) ;
break ;
case 230 :
sprintf ( name , " VarBstrFromUI2 " ) ;
break ;
case 231 :
sprintf ( name , " VarBstrFromUI4 " ) ;
break ;
case 232 :
sprintf ( name , " VarBstrFromDec " ) ;
break ;
case 233 :
sprintf ( name , " VarBoolFromI1 " ) ;
break ;
case 234 :
sprintf ( name , " VarBoolFromUI2 " ) ;
break ;
case 235 :
sprintf ( name , " VarBoolFromUI4 " ) ;
break ;
case 236 :
sprintf ( name , " VarBoolFromDec " ) ;
break ;
case 237 :
sprintf ( name , " VarUI1FromI1 " ) ;
break ;
case 238 :
sprintf ( name , " VarUI1FromUI2 " ) ;
break ;
case 239 :
sprintf ( name , " VarUI1FromUI4 " ) ;
break ;
case 240 :
sprintf ( name , " VarUI1FromDec " ) ;
break ;
case 241 :
sprintf ( name , " VarDecFromI1 " ) ;
break ;
case 242 :
sprintf ( name , " VarDecFromUI2 " ) ;
break ;
case 243 :
sprintf ( name , " VarDecFromUI4 " ) ;
break ;
case 244 :
sprintf ( name , " VarI1FromUI1 " ) ;
break ;
case 245 :
sprintf ( name , " VarI1FromI2 " ) ;
break ;
case 246 :
sprintf ( name , " VarI1FromI4 " ) ;
break ;
case 247 :
sprintf ( name , " VarI1FromR4 " ) ;
break ;
case 248 :
sprintf ( name , " VarI1FromR8 " ) ;
break ;
case 249 :
sprintf ( name , " VarI1FromDate " ) ;
break ;
case 250 :
sprintf ( name , " VarI1FromCy " ) ;
break ;
case 251 :
sprintf ( name , " VarI1FromStr " ) ;
break ;
case 252 :
sprintf ( name , " VarI1FromDisp " ) ;
break ;
case 253 :
sprintf ( name , " VarI1FromBool " ) ;
break ;
case 254 :
sprintf ( name , " VarI1FromUI2 " ) ;
break ;
case 255 :
sprintf ( name , " VarI1FromUI4 " ) ;
break ;
case 256 :
sprintf ( name , " VarI1FromDec " ) ;
break ;
case 257 :
sprintf ( name , " VarUI2FromUI1 " ) ;
break ;
case 258 :
sprintf ( name , " VarUI2FromI2 " ) ;
break ;
case 259 :
sprintf ( name , " VarUI2FromI4 " ) ;
break ;
case 260 :
sprintf ( name , " VarUI2FromR4 " ) ;
break ;
case 261 :
sprintf ( name , " VarUI2FromR8 " ) ;
break ;
case 262 :
sprintf ( name , " VarUI2FromDate " ) ;
break ;
case 263 :
sprintf ( name , " VarUI2FromCy " ) ;
break ;
case 264 :
sprintf ( name , " VarUI2FromStr " ) ;
break ;
case 265 :
sprintf ( name , " VarUI2FromDisp " ) ;
break ;
case 266 :
sprintf ( name , " VarUI2FromBool " ) ;
break ;
case 267 :
sprintf ( name , " VarUI2FromI1 " ) ;
break ;
case 268 :
sprintf ( name , " VarUI2FromUI4 " ) ;
break ;
case 269 :
sprintf ( name , " VarUI2FromDec " ) ;
break ;
case 270 :
sprintf ( name , " VarUI4FromUI1 " ) ;
break ;
case 271 :
sprintf ( name , " VarUI4FromI2 " ) ;
break ;
case 272 :
sprintf ( name , " VarUI4FromI4 " ) ;
break ;
case 273 :
sprintf ( name , " VarUI4FromR4 " ) ;
break ;
case 274 :
sprintf ( name , " VarUI4FromR8 " ) ;
break ;
case 275 :
sprintf ( name , " VarUI4FromDate " ) ;
break ;
case 276 :
sprintf ( name , " VarUI4FromCy " ) ;
break ;
case 277 :
sprintf ( name , " VarUI4FromStr " ) ;
break ;
case 278 :
sprintf ( name , " VarUI4FromDisp " ) ;
break ;
case 279 :
sprintf ( name , " VarUI4FromBool " ) ;
break ;
case 280 :
sprintf ( name , " VarUI4FromI1 " ) ;
break ;
case 281 :
sprintf ( name , " VarUI4FromUI2 " ) ;
break ;
case 282 :
sprintf ( name , " VarUI4FromDec " ) ;
break ;
case 283 :
sprintf ( name , " BSTR_UserSize " ) ;
break ;
case 284 :
sprintf ( name , " BSTR_UserMarshal " ) ;
break ;
case 285 :
sprintf ( name , " BSTR_UserUnmarshal " ) ;
break ;
case 286 :
sprintf ( name , " BSTR_UserFree " ) ;
break ;
case 287 :
sprintf ( name , " VARIANT_UserSize " ) ;
break ;
case 288 :
sprintf ( name , " VARIANT_UserMarshal " ) ;
break ;
case 289 :
sprintf ( name , " VARIANT_UserUnmarshal " ) ;
break ;
case 290 :
sprintf ( name , " VARIANT_UserFree " ) ;
break ;
case 291 :
sprintf ( name , " LPSAFEARRAY_UserSize " ) ;
break ;
case 292 :
sprintf ( name , " LPSAFEARRAY_UserMarshal " ) ;
break ;
case 293 :
sprintf ( name , " LPSAFEARRAY_UserUnmarshal " ) ;
break ;
case 294 :
sprintf ( name , " LPSAFEARRAY_UserFree " ) ;
break ;
case 295 :
sprintf ( name , " LPSAFEARRAY_Size " ) ;
break ;
case 296 :
sprintf ( name , " LPSAFEARRAY_Marshal " ) ;
break ;
case 297 :
sprintf ( name , " LPSAFEARRAY_Unmarshal " ) ;
break ;
case 298 :
sprintf ( name , " VarDecCmpR8 " ) ;
break ;
case 299 :
sprintf ( name , " VarCyAdd " ) ;
break ;
case 300 :
sprintf ( name , " DllUnregisterServer " ) ;
break ;
case 301 :
sprintf ( name , " OACreateTypeLib2 " ) ;
break ;
case 303 :
sprintf ( name , " VarCyMul " ) ;
break ;
case 304 :
sprintf ( name , " VarCyMulI4 " ) ;
break ;
case 305 :
sprintf ( name , " VarCySub " ) ;
break ;
case 306 :
sprintf ( name , " VarCyAbs " ) ;
break ;
case 307 :
sprintf ( name , " VarCyFix " ) ;
break ;
case 308 :
sprintf ( name , " VarCyInt " ) ;
break ;
case 309 :
sprintf ( name , " VarCyNeg " ) ;
break ;
case 310 :
sprintf ( name , " VarCyRound " ) ;
break ;
case 311 :
sprintf ( name , " VarCyCmp " ) ;
break ;
case 312 :
sprintf ( name , " VarCyCmpR8 " ) ;
break ;
case 313 :
sprintf ( name , " VarBstrCat " ) ;
break ;
case 314 :
sprintf ( name , " VarBstrCmp " ) ;
break ;
case 315 :
sprintf ( name , " VarR8Pow " ) ;
break ;
case 316 :
sprintf ( name , " VarR4CmpR8 " ) ;
break ;
case 317 :
sprintf ( name , " VarR8Round " ) ;
break ;
case 318 :
sprintf ( name , " VarCat " ) ;
break ;
case 319 :
sprintf ( name , " VarDateFromUdateEx " ) ;
break ;
case 322 :
sprintf ( name , " GetRecordInfoFromGuids " ) ;
break ;
case 323 :
sprintf ( name , " GetRecordInfoFromTypeInfo " ) ;
break ;
case 325 :
sprintf ( name , " SetVarConversionLocaleSetting " ) ;
break ;
case 326 :
sprintf ( name , " GetVarConversionLocaleSetting " ) ;
break ;
case 327 :
sprintf ( name , " SetOaNoCache " ) ;
break ;
case 329 :
sprintf ( name , " VarCyMulI8 " ) ;
break ;
case 330 :
sprintf ( name , " VarDateFromUdate " ) ;
break ;
case 331 :
sprintf ( name , " VarUdateFromDate " ) ;
break ;
case 332 :
sprintf ( name , " GetAltMonthNames " ) ;
break ;
case 333 :
sprintf ( name , " VarI8FromUI1 " ) ;
break ;
case 334 :
sprintf ( name , " VarI8FromI2 " ) ;
break ;
case 335 :
sprintf ( name , " VarI8FromR4 " ) ;
break ;
case 336 :
sprintf ( name , " VarI8FromR8 " ) ;
break ;
case 337 :
sprintf ( name , " VarI8FromCy " ) ;
break ;
case 338 :
sprintf ( name , " VarI8FromDate " ) ;
break ;
case 339 :
sprintf ( name , " VarI8FromStr " ) ;
break ;
case 340 :
sprintf ( name , " VarI8FromDisp " ) ;
break ;
case 341 :
sprintf ( name , " VarI8FromBool " ) ;
break ;
case 342 :
sprintf ( name , " VarI8FromI1 " ) ;
break ;
case 343 :
sprintf ( name , " VarI8FromUI2 " ) ;
break ;
case 344 :
sprintf ( name , " VarI8FromUI4 " ) ;
break ;
case 345 :
sprintf ( name , " VarI8FromDec " ) ;
break ;
case 346 :
sprintf ( name , " VarI2FromI8 " ) ;
break ;
case 347 :
sprintf ( name , " VarI2FromUI8 " ) ;
break ;
case 348 :
sprintf ( name , " VarI4FromI8 " ) ;
break ;
case 349 :
sprintf ( name , " VarI4FromUI8 " ) ;
break ;
case 360 :
sprintf ( name , " VarR4FromI8 " ) ;
break ;
case 361 :
sprintf ( name , " VarR4FromUI8 " ) ;
break ;
case 362 :
sprintf ( name , " VarR8FromI8 " ) ;
break ;
case 363 :
sprintf ( name , " VarR8FromUI8 " ) ;
break ;
case 364 :
sprintf ( name , " VarDateFromI8 " ) ;
break ;
case 365 :
sprintf ( name , " VarDateFromUI8 " ) ;
break ;
case 366 :
sprintf ( name , " VarCyFromI8 " ) ;
break ;
case 367 :
sprintf ( name , " VarCyFromUI8 " ) ;
break ;
case 368 :
sprintf ( name , " VarBstrFromI8 " ) ;
break ;
case 369 :
sprintf ( name , " VarBstrFromUI8 " ) ;
break ;
case 370 :
sprintf ( name , " VarBoolFromI8 " ) ;
break ;
case 371 :
sprintf ( name , " VarBoolFromUI8 " ) ;
break ;
case 372 :
sprintf ( name , " VarUI1FromI8 " ) ;
break ;
case 373 :
sprintf ( name , " VarUI1FromUI8 " ) ;
break ;
case 374 :
sprintf ( name , " VarDecFromI8 " ) ;
break ;
case 375 :
sprintf ( name , " VarDecFromUI8 " ) ;
break ;
case 376 :
sprintf ( name , " VarI1FromI8 " ) ;
break ;
case 377 :
sprintf ( name , " VarI1FromUI8 " ) ;
break ;
case 378 :
sprintf ( name , " VarUI2FromI8 " ) ;
break ;
case 379 :
sprintf ( name , " VarUI2FromUI8 " ) ;
break ;
case 401 :
sprintf ( name , " OleLoadPictureEx " ) ;
break ;
case 402 :
sprintf ( name , " OleLoadPictureFileEx " ) ;
break ;
case 411 :
sprintf ( name , " SafeArrayCreateVector " ) ;
break ;
case 412 :
sprintf ( name , " SafeArrayCopyData " ) ;
break ;
case 413 :
sprintf ( name , " VectorFromBstr " ) ;
break ;
case 414 :
sprintf ( name , " BstrFromVector " ) ;
break ;
case 415 :
sprintf ( name , " OleIconToCursor " ) ;
break ;
case 416 :
sprintf ( name , " OleCreatePropertyFrameIndirect " ) ;
break ;
case 417 :
sprintf ( name , " OleCreatePropertyFrame " ) ;
break ;
case 418 :
sprintf ( name , " OleLoadPicture " ) ;
break ;
case 419 :
sprintf ( name , " OleCreatePictureIndirect " ) ;
break ;
case 420 :
sprintf ( name , " OleCreateFontIndirect " ) ;
break ;
case 421 :
sprintf ( name , " OleTranslateColor " ) ;
break ;
case 422 :
sprintf ( name , " OleLoadPictureFile " ) ;
break ;
case 423 :
sprintf ( name , " OleSavePictureFile " ) ;
break ;
case 424 :
sprintf ( name , " OleLoadPicturePath " ) ;
break ;
case 425 :
sprintf ( name , " VarUI4FromI8 " ) ;
break ;
case 426 :
sprintf ( name , " VarUI4FromUI8 " ) ;
break ;
case 427 :
sprintf ( name , " VarI8FromUI8 " ) ;
break ;
case 428 :
sprintf ( name , " VarUI8FromI8 " ) ;
break ;
case 429 :
sprintf ( name , " VarUI8FromUI1 " ) ;
break ;
case 430 :
sprintf ( name , " VarUI8FromI2 " ) ;
break ;
case 431 :
sprintf ( name , " VarUI8FromR4 " ) ;
break ;
case 432 :
sprintf ( name , " VarUI8FromR8 " ) ;
break ;
case 433 :
sprintf ( name , " VarUI8FromCy " ) ;
break ;
case 434 :
sprintf ( name , " VarUI8FromDate " ) ;
break ;
case 435 :
sprintf ( name , " VarUI8FromStr " ) ;
break ;
case 436 :
sprintf ( name , " VarUI8FromDisp " ) ;
break ;
case 437 :
sprintf ( name , " VarUI8FromBool " ) ;
break ;
case 438 :
sprintf ( name , " VarUI8FromI1 " ) ;
break ;
case 439 :
sprintf ( name , " VarUI8FromUI2 " ) ;
break ;
case 440 :
sprintf ( name , " VarUI8FromUI4 " ) ;
break ;
case 441 :
sprintf ( name , " VarUI8FromDec " ) ;
break ;
case 442 :
sprintf ( name , " RegisterTypeLibForUser " ) ;
break ;
case 443 :
sprintf ( name , " UnRegisterTypeLibForUser " ) ;
break ;
default :
break ;
}
}
if ( name [ 0 ] = = ' \0 ' )
sprintf ( name , " ord%u " , ord ) ;
return cli_strdup ( name ) ;
}
static int validate_impname ( const char * name , uint32_t length , int dll )
{
uint32_t i = 0 ;
const char * c = name ;
if ( ! name | | length = = 0 )
return 1 ;
while ( i < length & & * c ! = ' \0 ' ) {
if ( ( * c > = ' 0 ' & & * c < = ' 9 ' ) | |
( * c > = ' a ' & & * c < = ' z ' ) | |
( * c > = ' A ' & & * c < = ' Z ' ) | |
( * c = = ' _ ' ) | |
( dll & & * c = = ' . ' ) ) {
c + + ;
i + + ;
} else
return 0 ;
}
return 1 ;
}
static inline int hash_impfns ( cli_ctx * ctx , void * * hashctx , uint32_t * impsz , struct pe_image_import_descriptor * image , const char * dllname , struct cli_exe_info * peinfo , int * first )
{
uint32_t thuoff = 0 , offset ;
fmap_t * map = ctx - > fmap ;
size_t dlllen = 0 , fsize = map - > len ;
unsigned int err = 0 ;
int num_fns = 0 , ret = CL_SUCCESS ;
const char * buffer ;
2023-01-14 18:28:39 +08:00
cli_hash_type_t type ;
2022-10-22 18:41:00 +08:00
# if HAVE_JSON
json_object * imptbl = NULL ;
# else
void * imptbl = NULL ;
# endif
if ( image - > u . OriginalFirstThunk )
thuoff = cli_rawaddr ( image - > u . OriginalFirstThunk , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( err | | thuoff = = 0 )
thuoff = cli_rawaddr ( image - > FirstThunk , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( err ) {
cli_dbgmsg ( " scan_pe: invalid rva for image first thunk \n " ) ;
return CL_EFORMAT ;
}
# if HAVE_JSON
if ( ctx - > wrkproperty ) {
imptbl = cli_jsonarray ( ctx - > wrkproperty , " ImportTable " ) ;
if ( ! imptbl ) {
cli_dbgmsg ( " scan_pe: cannot allocate import table json object \n " ) ;
return CL_EMEM ;
}
}
# endif
# define UPDATE_IMPHASH() \
do { \
if ( funcname ) { \
size_t i , j ; \
char * fname ; \
size_t funclen ; \
\
if ( dlllen = = 0 ) { \
char * ext = strstr ( dllname , " . " ) ; \
\
if ( ext & & ( strncasecmp ( ext , " .ocx " , 4 ) = = 0 | | \
strncasecmp ( ext , " .sys " , 4 ) = = 0 | | \
strncasecmp ( ext , " .dll " , 4 ) = = 0 ) ) \
dlllen = ext - dllname ; \
else \
dlllen = strlen ( dllname ) ; \
} \
\
funclen = strlen ( funcname ) ; \
if ( validate_impname ( funcname , funclen , 1 ) = = 0 ) { \
cli_dbgmsg ( " scan_pe: invalid name for imported function \n " ) ; \
ret = CL_EFORMAT ; \
break ; \
} \
\
fname = cli_calloc ( funclen + dlllen + 3 , sizeof ( char ) ) ; \
if ( fname = = NULL ) { \
cli_dbgmsg ( " scan_pe: cannot allocate memory for imphash string \n " ) ; \
ret = CL_EMEM ; \
break ; \
} \
j = 0 ; \
if ( ! * first ) \
fname [ j + + ] = ' , ' ; \
for ( i = 0 ; i < dlllen ; i + + , j + + ) \
fname [ j ] = tolower ( dllname [ i ] ) ; \
fname [ j + + ] = ' . ' ; \
for ( i = 0 ; i < funclen ; i + + , j + + ) \
fname [ j ] = tolower ( funcname [ i ] ) ; \
\
if ( imptbl ) { \
char * jname = * first ? fname : fname + 1 ; \
cli_jsonstr ( imptbl , NULL , jname ) ; \
} \
\
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + ) \
cl_update_hash ( hashctx [ type ] , fname , strlen ( fname ) ) ; \
* impsz + = strlen ( fname ) ; \
\
* first = 0 ; \
free ( fname ) ; \
} \
} while ( 0 )
if ( ! peinfo - > is_pe32plus ) {
struct pe_image_thunk32 thunk32 ;
while ( ( num_fns < PE_MAXIMPORTS ) & & ( fmap_readn ( map , & thunk32 , thuoff , sizeof ( struct pe_image_thunk32 ) ) = = sizeof ( struct pe_image_thunk32 ) ) & & ( thunk32 . u . Ordinal ! = 0 ) ) {
char * funcname = NULL ;
thuoff + = sizeof ( struct pe_image_thunk32 ) ;
thunk32 . u . Ordinal = EC32 ( thunk32 . u . Ordinal ) ;
if ( ! ( thunk32 . u . Ordinal & PE_IMAGEDIR_ORDINAL_FLAG32 ) ) {
offset = cli_rawaddr ( thunk32 . u . Function , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( ! ret ) {
/* Hint field is a uint16_t and precedes the Name field */
if ( ( buffer = fmap_need_off_once ( map , offset + sizeof ( uint16_t ) , MIN ( PE_MAXNAMESIZE , fsize - offset ) ) ) ! = NULL ) {
funcname = CLI_STRNDUP ( buffer , MIN ( PE_MAXNAMESIZE , fsize - offset ) ) ;
if ( funcname = = NULL ) {
cli_dbgmsg ( " scan_pe: cannot duplicate function name \n " ) ;
return CL_EMEM ;
}
}
}
} else {
/* ordinal lookup */
funcname = pe_ordinal ( dllname , thunk32 . u . Ordinal & 0xFFFF ) ;
if ( funcname = = NULL ) {
cli_dbgmsg ( " scan_pe: cannot duplicate function name \n " ) ;
return CL_EMEM ;
}
}
UPDATE_IMPHASH ( ) ;
free ( funcname ) ;
if ( ret ! = CL_SUCCESS )
return ret ;
}
} else {
struct pe_image_thunk64 thunk64 ;
while ( ( num_fns < PE_MAXIMPORTS ) & & ( fmap_readn ( map , & thunk64 , thuoff , sizeof ( struct pe_image_thunk64 ) ) = = sizeof ( struct pe_image_thunk64 ) ) & & ( thunk64 . u . Ordinal ! = 0 ) ) {
char * funcname = NULL ;
thuoff + = sizeof ( struct pe_image_thunk64 ) ;
thunk64 . u . Ordinal = EC64 ( thunk64 . u . Ordinal ) ;
if ( ! ( thunk64 . u . Ordinal & PE_IMAGEDIR_ORDINAL_FLAG64 ) ) {
offset = cli_rawaddr ( thunk64 . u . Function , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( ! err ) {
/* Hint field is a uint16_t and precedes the Name field */
if ( ( buffer = fmap_need_off_once ( map , offset + sizeof ( uint16_t ) , MIN ( PE_MAXNAMESIZE , fsize - offset ) ) ) ! = NULL ) {
funcname = CLI_STRNDUP ( buffer , MIN ( PE_MAXNAMESIZE , fsize - offset ) ) ;
if ( funcname = = NULL ) {
cli_dbgmsg ( " scan_pe: cannot duplicate function name \n " ) ;
return CL_EMEM ;
}
}
}
} else {
/* ordinal lookup */
funcname = pe_ordinal ( dllname , thunk64 . u . Ordinal & 0xFFFF ) ;
if ( funcname = = NULL ) {
cli_dbgmsg ( " scan_pe: cannot duplicate function name \n " ) ;
return CL_EMEM ;
}
}
UPDATE_IMPHASH ( ) ;
free ( funcname ) ;
if ( ret ! = CL_SUCCESS )
return ret ;
}
}
return CL_SUCCESS ;
}
static cl_error_t hash_imptbl ( cli_ctx * ctx , unsigned char * * digest , uint32_t * impsz , int * genhash , struct cli_exe_info * peinfo )
{
2023-01-14 18:28:39 +08:00
cl_error_t status = CL_ERROR ;
cl_error_t ret ;
struct pe_image_import_descriptor image = { 0 } ;
const struct pe_image_import_descriptor * impdes ;
2022-10-22 18:41:00 +08:00
fmap_t * map = ctx - > fmap ;
size_t left , fsize = map - > len ;
uint32_t impoff , offset ;
2023-01-14 18:28:39 +08:00
const char * buffer ;
void * hashctx [ CLI_HASH_AVAIL_TYPES ] = { 0 } ;
cli_hash_type_t type ;
int nimps = 0 ;
2022-10-22 18:41:00 +08:00
unsigned int err ;
2023-01-14 18:28:39 +08:00
int first = 1 ;
bool needed_impoff = false ;
2022-10-22 18:41:00 +08:00
/* If the PE doesn't have an import table then skip it. This is an
* uncommon case but can happen . */
if ( peinfo - > dirs [ 1 ] . VirtualAddress = = 0 | | peinfo - > dirs [ 1 ] . Size = = 0 ) {
cli_dbgmsg ( " scan_pe: import table data dir does not exist (skipping .imp scanning) \n " ) ;
2023-01-14 18:28:39 +08:00
status = CL_BREAK ;
goto done ;
2022-10-22 18:41:00 +08:00
}
// TODO Add EC32 wrappers
impoff = cli_rawaddr ( peinfo - > dirs [ 1 ] . VirtualAddress , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( err | | impoff + peinfo - > dirs [ 1 ] . Size > fsize ) {
cli_dbgmsg ( " scan_pe: invalid rva for import table data \n " ) ;
2023-01-14 18:28:39 +08:00
status = CL_BREAK ;
goto done ;
2022-10-22 18:41:00 +08:00
}
// TODO Add EC32 wrapper
2023-01-14 18:28:39 +08:00
impdes = ( const struct pe_image_import_descriptor * ) fmap_need_off ( map , impoff , peinfo - > dirs [ 1 ] . Size ) ;
2022-10-22 18:41:00 +08:00
if ( impdes = = NULL ) {
cli_dbgmsg ( " scan_pe: failed to acquire fmap buffer \n " ) ;
2023-01-14 18:28:39 +08:00
status = CL_EREAD ;
goto done ;
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
needed_impoff = true ;
/* Safety: We can trust peinfo->dirs[1].Size only because `fmap_need_off()` (above)
* would have failed if the size exceeds the end of the fmap . */
2022-10-22 18:41:00 +08:00
left = peinfo - > dirs [ 1 ] . Size ;
if ( genhash [ CLI_HASH_MD5 ] ) {
hashctx [ CLI_HASH_MD5 ] = cl_hash_init ( " md5 " ) ;
if ( hashctx [ CLI_HASH_MD5 ] = = NULL ) {
2023-01-14 18:28:39 +08:00
status = CL_EMEM ;
goto done ;
2022-10-22 18:41:00 +08:00
}
}
if ( genhash [ CLI_HASH_SHA1 ] ) {
hashctx [ CLI_HASH_SHA1 ] = cl_hash_init ( " sha1 " ) ;
if ( hashctx [ CLI_HASH_SHA1 ] = = NULL ) {
2023-01-14 18:28:39 +08:00
status = CL_EMEM ;
goto done ;
2022-10-22 18:41:00 +08:00
}
}
if ( genhash [ CLI_HASH_SHA256 ] ) {
hashctx [ CLI_HASH_SHA256 ] = cl_hash_init ( " sha256 " ) ;
if ( hashctx [ CLI_HASH_SHA256 ] = = NULL ) {
2023-01-14 18:28:39 +08:00
status = CL_EMEM ;
goto done ;
2022-10-22 18:41:00 +08:00
}
}
2023-01-14 18:28:39 +08:00
while ( left > sizeof ( struct pe_image_import_descriptor ) & & nimps < PE_MAXIMPORTS ) {
2022-10-22 18:41:00 +08:00
char * dllname = NULL ;
2023-01-14 18:28:39 +08:00
/* Get copy of image import descriptor to work with */
memcpy ( & image , impdes , sizeof ( struct pe_image_import_descriptor ) ) ;
if ( image . Name = = 0 ) {
// Name RVA is 0, which doesn't seem right. I guess we skip the rest?
// TODO: Is that right?
break ;
}
/* Prepare for next iteration, in case we need to `continue;` */
2022-10-22 18:41:00 +08:00
left - = sizeof ( struct pe_image_import_descriptor ) ;
nimps + + ;
2023-01-14 18:28:39 +08:00
impdes + + ;
2022-10-22 18:41:00 +08:00
/* Endian Conversion */
2023-01-14 18:28:39 +08:00
image . u . OriginalFirstThunk = EC32 ( image . u . OriginalFirstThunk ) ;
image . TimeDateStamp = EC32 ( image . TimeDateStamp ) ;
image . ForwarderChain = EC32 ( image . ForwarderChain ) ;
image . Name = EC32 ( image . Name ) ;
image . FirstThunk = EC32 ( image . FirstThunk ) ;
2022-10-22 18:41:00 +08:00
/* DLL name acquisition */
2023-01-14 18:28:39 +08:00
offset = cli_rawaddr ( image . Name , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
2022-10-22 18:41:00 +08:00
if ( err | | offset > fsize ) {
cli_dbgmsg ( " scan_pe: invalid rva for dll name \n " ) ;
/* TODO: ignore or return? */
/*
2023-01-14 18:28:39 +08:00
continue ;
2022-10-22 18:41:00 +08:00
*/
2023-01-14 18:28:39 +08:00
status = CL_EFORMAT ;
goto done ;
2022-10-22 18:41:00 +08:00
}
buffer = fmap_need_off_once ( map , offset , MIN ( PE_MAXNAMESIZE , fsize - offset ) ) ;
if ( buffer = = NULL ) {
cli_dbgmsg ( " scan_pe: failed to read name for dll \n " ) ;
2023-01-14 18:28:39 +08:00
status = CL_EREAD ;
goto done ;
2022-10-22 18:41:00 +08:00
}
if ( validate_impname ( dllname , MIN ( PE_MAXNAMESIZE , fsize - offset ) , 1 ) = = 0 ) {
cli_dbgmsg ( " scan_pe: invalid name for imported dll \n " ) ;
2023-01-14 18:28:39 +08:00
status = CL_EFORMAT ;
goto done ;
2022-10-22 18:41:00 +08:00
}
dllname = CLI_STRNDUP ( buffer , MIN ( PE_MAXNAMESIZE , fsize - offset ) ) ;
if ( dllname = = NULL ) {
cli_dbgmsg ( " scan_pe: cannot duplicate dll name \n " ) ;
2023-01-14 18:28:39 +08:00
status = CL_EMEM ;
goto done ;
2022-10-22 18:41:00 +08:00
}
/* DLL function handling - inline function */
2023-01-14 18:28:39 +08:00
ret = hash_impfns ( ctx , hashctx , impsz , & image , dllname , peinfo , & first ) ;
2022-10-22 18:41:00 +08:00
free ( dllname ) ;
dllname = NULL ;
2023-01-14 18:28:39 +08:00
if ( ret ! = CL_SUCCESS ) {
status = ret ;
goto done ;
}
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + ) {
2022-10-22 18:41:00 +08:00
cl_finish_hash ( hashctx [ type ] , digest [ type ] ) ;
2023-01-14 18:28:39 +08:00
hashctx [ type ] = NULL ;
}
status = CL_SUCCESS ;
done :
if ( needed_impoff ) {
fmap_unneed_off ( map , impoff , peinfo - > dirs [ 1 ] . Size ) ;
}
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + ) {
if ( NULL ! = hashctx [ type ] ) {
cl_hash_destroy ( hashctx [ type ] ) ;
}
}
return status ;
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
static cl_error_t scan_pe_imp ( cli_ctx * ctx , struct cli_exe_info * peinfo )
2022-10-22 18:41:00 +08:00
{
struct cli_matcher * imp = ctx - > engine - > hm_imp ;
unsigned char * hashset [ CLI_HASH_AVAIL_TYPES ] ;
const char * virname = NULL ;
int genhash [ CLI_HASH_AVAIL_TYPES ] ;
uint32_t impsz = 0 ;
2023-01-14 18:28:39 +08:00
cli_hash_type_t type ;
cl_error_t ret = CL_CLEAN ;
2022-10-22 18:41:00 +08:00
/* pick hashtypes to generate */
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + ) {
genhash [ type ] = cli_hm_have_any ( imp , type ) ;
if ( genhash [ type ] ) {
hashset [ type ] = cli_malloc ( hashlen [ type ] ) ;
if ( ! hashset [ type ] ) {
cli_errmsg ( " scan_pe: cli_malloc failed! \n " ) ;
for ( ; type > 0 ; )
free ( hashset [ - - type ] ) ;
return CL_EMEM ;
}
} else {
hashset [ type ] = NULL ;
}
}
/* Force md5 hash generation for debug and preclass */
# if HAVE_JSON
if ( ( cli_debug_flag | | ctx - > wrkproperty ) & & ! genhash [ CLI_HASH_MD5 ] ) {
# else
if ( cli_debug_flag & & ! genhash [ CLI_HASH_MD5 ] ) {
# endif
genhash [ CLI_HASH_MD5 ] = 1 ;
hashset [ CLI_HASH_MD5 ] = cli_calloc ( hashlen [ CLI_HASH_MD5 ] , sizeof ( char ) ) ;
if ( ! hashset [ CLI_HASH_MD5 ] ) {
cli_errmsg ( " scan_pe: cli_malloc failed! \n " ) ;
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + )
free ( hashset [ type ] ) ;
return CL_EMEM ;
}
}
/* Generate hashes */
ret = hash_imptbl ( ctx , hashset , & impsz , genhash , peinfo ) ;
if ( ret ! = CL_SUCCESS ) {
2023-01-14 18:28:39 +08:00
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + ) {
2022-10-22 18:41:00 +08:00
free ( hashset [ type ] ) ;
2023-01-14 18:28:39 +08:00
}
if ( ret = = CL_BREAK ) {
ret = CL_SUCCESS ;
}
2022-10-22 18:41:00 +08:00
return ret ;
}
/* Print hash */
# if HAVE_JSON
if ( cli_debug_flag | | ctx - > wrkproperty ) {
# else
if ( cli_debug_flag ) {
# endif
char * dstr = cli_str2hex ( ( char * ) hashset [ CLI_HASH_MD5 ] , hashlen [ CLI_HASH_MD5 ] ) ;
cli_dbgmsg ( " IMP: %s:%u \n " , dstr ? ( char * ) dstr : " (NULL) " , impsz ) ;
# if HAVE_JSON
if ( ctx - > wrkproperty )
cli_jsonstr ( ctx - > wrkproperty , " Imphash " , dstr ? dstr : " (NULL) " ) ;
# endif
if ( dstr )
free ( dstr ) ;
}
/* Do scans */
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + ) {
if ( cli_hm_scan ( hashset [ type ] , impsz , & virname , imp , type ) = = CL_VIRUS ) {
ret = cli_append_virus ( ctx , virname ) ;
2023-01-14 18:28:39 +08:00
if ( ret ! = CL_SUCCESS ) {
break ;
2022-10-22 18:41:00 +08:00
}
}
if ( cli_hm_scan_wild ( hashset [ type ] , & virname , imp , type ) = = CL_VIRUS ) {
cli_append_virus ( ctx , virname ) ;
2023-01-14 18:28:39 +08:00
if ( ret ! = CL_SUCCESS ) {
break ;
2022-10-22 18:41:00 +08:00
}
}
}
for ( type = CLI_HASH_MD5 ; type < CLI_HASH_AVAIL_TYPES ; type + + )
free ( hashset [ type ] ) ;
return ret ;
}
# if HAVE_JSON
static struct json_object * get_pe_property ( cli_ctx * ctx )
{
struct json_object * pe ;
if ( ! ( ctx ) | | ! ( ctx - > wrkproperty ) )
return NULL ;
if ( ! json_object_object_get_ex ( ctx - > wrkproperty , " PE " , & pe ) ) {
pe = json_object_new_object ( ) ;
if ( ! ( pe ) )
return NULL ;
json_object_object_add ( ctx - > wrkproperty , " PE " , pe ) ;
}
return pe ;
}
static void pe_add_heuristic_property ( cli_ctx * ctx , const char * key )
{
struct json_object * heuristics ;
struct json_object * pe ;
struct json_object * str ;
pe = get_pe_property ( ctx ) ;
if ( ! ( pe ) )
return ;
if ( ! json_object_object_get_ex ( pe , " Heuristics " , & heuristics ) ) {
heuristics = json_object_new_array ( ) ;
if ( ! ( heuristics ) )
return ;
json_object_object_add ( pe , " Heuristics " , heuristics ) ;
}
str = json_object_new_string ( key ) ;
if ( ! ( str ) )
return ;
json_object_array_add ( heuristics , str ) ;
}
static struct json_object * get_section_json ( cli_ctx * ctx )
{
struct json_object * pe ;
struct json_object * section ;
pe = get_pe_property ( ctx ) ;
if ( ! ( pe ) )
return NULL ;
if ( ! json_object_object_get_ex ( pe , " Sections " , & section ) ) {
section = json_object_new_array ( ) ;
if ( ! ( section ) )
return NULL ;
json_object_object_add ( pe , " Sections " , section ) ;
}
return section ;
}
static void add_section_info ( cli_ctx * ctx , struct cli_exe_section * s )
{
struct json_object * sections , * section , * obj ;
char address [ 16 ] ;
sections = get_section_json ( ctx ) ;
if ( ! ( sections ) )
return ;
section = json_object_new_object ( ) ;
if ( ! ( section ) )
return ;
obj = json_object_new_int ( ( int32_t ) ( s - > rsz ) ) ;
if ( ! ( obj ) )
return ;
json_object_object_add ( section , " RawSize " , obj ) ;
obj = json_object_new_int ( ( int32_t ) ( s - > raw ) ) ;
if ( ! ( obj ) )
return ;
json_object_object_add ( section , " RawOffset " , obj ) ;
snprintf ( address , sizeof ( address ) , " 0x%08x " , s - > rva ) ;
obj = json_object_new_string ( address ) ;
if ( ! ( obj ) )
return ;
json_object_object_add ( section , " VirtualAddress " , obj ) ;
obj = json_object_new_boolean ( ( s - > chr & 0x20000000 ) = = 0x20000000 ) ;
if ( ( obj ) )
json_object_object_add ( section , " Executable " , obj ) ;
obj = json_object_new_boolean ( ( s - > chr & 0x80000000 ) = = 0x80000000 ) ;
if ( ( obj ) )
json_object_object_add ( section , " Writable " , obj ) ;
obj = json_object_new_boolean ( s - > urva > > 31 | | s - > uvsz > > 31 | | ( s - > rsz & & s - > uraw > > 31 ) | | s - > ursz > > 31 ) ;
if ( ( obj ) )
json_object_object_add ( section , " Signed " , obj ) ;
json_object_array_add ( sections , section ) ;
}
# endif
int cli_scanpe ( cli_ctx * ctx )
{
uint8_t polipos = 0 ;
char epbuff [ 4096 ] , * tempfile ;
size_t epsize ;
size_t bytes ;
unsigned int i , j , found , upx_success = 0 , err ;
unsigned int ssize = 0 , dsize = 0 , corrupted_cur ;
int ( * upxfn ) ( const char * , uint32_t , char * , uint32_t * , uint32_t , uint32_t , uint32_t ) = NULL ;
const char * src = NULL ;
char * dest = NULL ;
2023-01-14 18:28:39 +08:00
int ndesc ;
cl_error_t ret = CL_SUCCESS ;
cl_error_t peheader_ret ;
int upack = 0 ;
2022-10-22 18:41:00 +08:00
size_t fsize ;
struct cli_bc_ctx * bc_ctx ;
fmap_t * map ;
struct cli_pe_hook_data pedata ;
# ifdef HAVE__INTERNAL__SHA_COLLECT
int sha_collect = ctx - > sha_collect ;
# endif
# if HAVE_JSON
int toval = 0 ;
struct json_object * pe_json = NULL ;
# endif
if ( ! ctx ) {
cli_errmsg ( " cli_scanpe: ctx == NULL \n " ) ;
return CL_ENULLARG ;
}
# if HAVE_JSON
if ( cli_json_timeout_cycle_check ( ctx , & toval ) ! = CL_SUCCESS ) {
return CL_ETIMEOUT ;
}
if ( SCAN_COLLECT_METADATA ) {
pe_json = get_pe_property ( ctx ) ;
}
# endif
map = ctx - > fmap ;
fsize = map - > len ;
struct cli_exe_info _peinfo ;
struct cli_exe_info * peinfo = & _peinfo ;
uint32_t opts = CLI_PEHEADER_OPT_DBG_PRINT_INFO | CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS ;
# if HAVE_JSON
if ( SCAN_COLLECT_METADATA ) {
opts | = CLI_PEHEADER_OPT_COLLECT_JSON ;
}
# endif
if ( DETECT_BROKEN_PE ) {
opts | = CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS ;
}
cli_exe_info_init ( peinfo , 0 ) ;
2023-01-14 18:28:39 +08:00
peheader_ret = cli_peheader ( map , peinfo , opts , ctx ) ;
2022-10-22 18:41:00 +08:00
// Warn the user if PE header parsing failed - if it's a binary that runs
// successfully on Windows, we need to relax our PE parsing standards so
// that we make sure the executable gets scanned appropriately
2023-01-14 18:28:39 +08:00
switch ( peheader_ret ) {
case CL_EFORMAT :
ret = CL_SUCCESS ;
if ( DETECT_BROKEN_PE ) {
ret = cli_append_potentially_unwanted ( ctx , " Heuristics.Broken.Executable " ) ;
}
cli_dbgmsg ( " cli_scanpe: PE header appears broken - won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking \n " ) ;
cli_exe_info_destroy ( peinfo ) ;
return ret ;
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
case CL_ERROR :
ret = CL_SUCCESS ;
cli_dbgmsg ( " cli_scanpe: An error occurred when parsing the PE header - won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking \n " ) ;
2022-10-22 18:41:00 +08:00
cli_exe_info_destroy ( peinfo ) ;
return ret ;
2023-01-14 18:28:39 +08:00
case CL_ETIMEOUT :
ret = CL_ETIMEOUT ;
cli_dbgmsg ( " cli_scanpe: JSON creation timed out - won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking \n " ) ;
cli_exe_info_destroy ( peinfo ) ;
return ret ;
default :
break ;
2022-10-22 18:41:00 +08:00
}
if ( ! peinfo - > is_pe32plus ) { /* PE */
2023-01-14 18:28:39 +08:00
if ( DCONF & PE_CONF_UPACK ) {
2022-10-22 18:41:00 +08:00
upack = ( EC16 ( peinfo - > file_hdr . SizeOfOptionalHeader ) = = 0x148 ) ;
2023-01-14 18:28:39 +08:00
}
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
for ( i = 0 ; i < peinfo - > nsections ; i + + ) {
2022-10-22 18:41:00 +08:00
if ( peinfo - > sections [ i ] . rsz ) { /* Don't bother with virtual only sections */
// TODO Regarding the commented out check below:
// This used to check that the section name was NULL, but now that
// header parsing is done in cli_peheader (and since we don't yet
// make the section name availabe via peinfo->sections[]) it would
// be a pain to fetch the name here. Since this is the only place
// in cli_scanpe that needs the section name, and since I verified
// that detection still occurs for Polipos without this check,
// let's leave it commented out for now.
if ( SCAN_HEURISTICS & & ( DCONF & PE_CONF_POLIPOS ) & & /*!*peinfo->sections[i].sname &&*/ peinfo - > sections [ i ] . vsz > 40000 & & peinfo - > sections [ i ] . vsz < 70000 & & peinfo - > sections [ i ] . chr = = 0xe0000060 ) polipos = i ;
/* check hash section sigs */
if ( ( DCONF & PE_CONF_MD5SECT ) & & ctx - > engine - > hm_mdb ) {
ret = scan_pe_mdb ( ctx , & ( peinfo - > sections [ i ] ) ) ;
2023-01-14 18:28:39 +08:00
if ( ret ! = CL_SUCCESS ) {
if ( ret ! = CL_VIRUS ) {
2022-10-22 18:41:00 +08:00
cli_errmsg ( " cli_scanpe: scan_pe_mdb failed: %s! \n " , cl_strerror ( ret ) ) ;
2023-01-14 18:28:39 +08:00
}
2022-10-22 18:41:00 +08:00
cli_dbgmsg ( " ------------------------------------ \n " ) ;
cli_exe_info_destroy ( peinfo ) ;
return ret ;
}
}
}
}
// TODO Don't bail out here
if ( peinfo - > is_pe32plus ) { /* Do not continue for PE32+ files */
cli_exe_info_destroy ( peinfo ) ;
return CL_CLEAN ;
}
epsize = fmap_readn ( map , epbuff , peinfo - > ep , 4096 ) ;
if ( ( size_t ) - 1 = = epsize ) {
/* Do not continue, all future logic requires at least a partial read into epbuff */
cli_exe_info_destroy ( peinfo ) ;
return CL_CLEAN ;
}
/* Disasm scan disabled since it's now handled by the bytecode */
/* CLI_UNPTEMP("cli_scanpe: DISASM",(peinfo->sections,0)); */
/* if(disasmbuf((unsigned char*)epbuff, epsize, ndesc)) */
2023-01-14 18:28:39 +08:00
/* ret = cli_scan_desc(ndesc, ctx, CL_TYPE_PE_DISASM, true, NULL, AC_SCAN_VIR); */
2022-10-22 18:41:00 +08:00
/* close(ndesc); */
/* if(ret == CL_VIRUS) { */
/* cli_exe_info_destroy(peinfo); */
/* CLI_TMPUNLK(); */
/* free(tempfile); */
/* return ret; */
/* } */
/* CLI_TMPUNLK(); */
/* free(tempfile); */
if ( peinfo - > overlay_start & & peinfo - > overlay_size > 0 ) {
ret = cli_scanishield ( ctx , peinfo - > overlay_start , peinfo - > overlay_size ) ;
2023-01-14 18:28:39 +08:00
if ( ret ! = CL_SUCCESS ) {
2022-10-22 18:41:00 +08:00
cli_exe_info_destroy ( peinfo ) ;
return ret ;
}
}
pedata . nsections = peinfo - > nsections ;
pedata . ep = peinfo - > ep ;
pedata . offset = 0 ;
memcpy ( & pedata . file_hdr , & ( peinfo - > file_hdr ) , sizeof ( peinfo - > file_hdr ) ) ;
// TODO no need to copy both of these for each binary
memcpy ( & pedata . opt32 , & ( peinfo - > pe_opt . opt32 ) , sizeof ( peinfo - > pe_opt . opt32 ) ) ;
memcpy ( & pedata . opt64 , & ( peinfo - > pe_opt . opt64 ) , sizeof ( peinfo - > pe_opt . opt64 ) ) ;
memcpy ( & pedata . dirs , & ( peinfo - > dirs ) , sizeof ( peinfo - > dirs ) ) ;
// Gross
memcpy ( & pedata . opt32_dirs , & ( peinfo - > dirs ) , sizeof ( peinfo - > dirs ) ) ;
memcpy ( & pedata . opt64_dirs , & ( peinfo - > dirs ) , sizeof ( peinfo - > dirs ) ) ;
pedata . e_lfanew = peinfo - > e_lfanew ;
pedata . overlays = peinfo - > overlay_start ;
pedata . overlays_sz = peinfo - > overlay_size ;
pedata . hdr_size = peinfo - > hdr_size ;
/* Bytecode BC_PE_ALL hook */
bc_ctx = cli_bytecode_context_alloc ( ) ;
if ( ! bc_ctx ) {
cli_errmsg ( " cli_scanpe: can't allocate memory for bc_ctx \n " ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
cli_bytecode_context_setpe ( bc_ctx , & pedata , peinfo - > sections ) ;
cli_bytecode_context_setctx ( bc_ctx , ctx ) ;
ret = cli_bytecode_runhook ( ctx , ctx - > engine , bc_ctx , BC_PE_ALL , map ) ;
switch ( ret ) {
case CL_ENULLARG :
cli_warnmsg ( " cli_scanpe: NULL argument supplied \n " ) ;
break ;
case CL_VIRUS :
case CL_BREAK :
cli_exe_info_destroy ( peinfo ) ;
cli_bytecode_context_destroy ( bc_ctx ) ;
return ret = = CL_VIRUS ? CL_VIRUS : CL_CLEAN ;
2023-01-14 18:28:39 +08:00
default :
break ;
2022-10-22 18:41:00 +08:00
}
cli_bytecode_context_destroy ( bc_ctx ) ;
/* Attempt to run scans on import table */
/* Run if there are existing signatures and/or preclassing */
# if HAVE_JSON
if ( DCONF & PE_CONF_IMPTBL & & ( ctx - > engine - > hm_imp | | ctx - > wrkproperty ) ) {
# else
if ( DCONF & PE_CONF_IMPTBL & & ctx - > engine - > hm_imp ) {
# endif
ret = scan_pe_imp ( ctx , peinfo ) ;
switch ( ret ) {
case CL_SUCCESS :
break ;
case CL_ENULLARG :
cli_warnmsg ( " cli_scanpe: NULL argument supplied \n " ) ;
break ;
case CL_VIRUS :
case CL_BREAK :
cli_exe_info_destroy ( peinfo ) ;
return ret = = CL_VIRUS ? CL_VIRUS : CL_CLEAN ;
default :
cli_exe_info_destroy ( peinfo ) ;
return ret ;
}
}
/* Attempt to detect some popular polymorphic viruses */
/* W32.Parite.B */
if ( SCAN_HEURISTICS & & ( DCONF & PE_CONF_PARITE ) & & ! peinfo - > is_dll & & epsize = = 4096 & & peinfo - > ep = = peinfo - > sections [ peinfo - > nsections - 1 ] . raw ) {
const char * pt = cli_memstr ( epbuff , 4040 , " \x47 \x65 \x74 \x50 \x72 \x6f \x63 \x41 \x64 \x64 \x72 \x65 \x73 \x73 \x00 " , 15 ) ;
if ( pt ) {
pt + = 15 ;
if ( ( ( ( uint32_t ) cli_readint32 ( pt ) ^ ( uint32_t ) cli_readint32 ( pt + 4 ) ) = = 0x505a4f ) & & ( ( ( uint32_t ) cli_readint32 ( pt + 8 ) ^ ( uint32_t ) cli_readint32 ( pt + 12 ) ) = = 0xffffb ) & & ( ( ( uint32_t ) cli_readint32 ( pt + 16 ) ^ ( uint32_t ) cli_readint32 ( pt + 20 ) ) = = 0xb8 ) ) {
2023-01-14 18:28:39 +08:00
ret = cli_append_potentially_unwanted ( ctx , " Heuristics.W32.Parite.B " ) ;
if ( ret ! = CL_SUCCESS ) {
cli_exe_info_destroy ( peinfo ) ;
return ret ;
2022-10-22 18:41:00 +08:00
}
}
}
}
/* Kriz */
if ( SCAN_HEURISTICS & & ( DCONF & PE_CONF_KRIZ ) & & epsize > = 200 & & CLI_ISCONTAINED ( peinfo - > sections [ peinfo - > nsections - 1 ] . raw , peinfo - > sections [ peinfo - > nsections - 1 ] . rsz , peinfo - > ep , 0x0fd2 ) & & epbuff [ 1 ] = = ' \x9c ' & & epbuff [ 2 ] = = ' \x60 ' ) {
enum { KZSTRASH ,
KZSCDELTA ,
KZSPDELTA ,
KZSGETSIZE ,
KZSXORPRFX ,
KZSXOR ,
KZSDDELTA ,
KZSLOOP ,
KZSTOP } ;
uint8_t kzs [ ] = { KZSTRASH , KZSCDELTA , KZSPDELTA , KZSGETSIZE , KZSTRASH , KZSXORPRFX , KZSXOR , KZSTRASH , KZSDDELTA , KZSTRASH , KZSLOOP , KZSTOP } ;
uint8_t * kzstate = kzs ;
uint8_t * kzcode = ( uint8_t * ) epbuff + 3 ;
uint8_t kzdptr = 0xff , kzdsize = 0xff ;
int kzlen = 197 , kzinitlen = 0xffff , kzxorlen = - 1 ;
cli_dbgmsg ( " cli_scanpe: in kriz \n " ) ;
while ( * kzstate ! = KZSTOP ) {
uint8_t op ;
if ( kzlen < = 6 )
break ;
op = * kzcode + + ;
kzlen - - ;
switch ( * kzstate ) {
case KZSTRASH :
case KZSGETSIZE : {
int opsz = 0 ;
switch ( op ) {
case 0x81 :
kzcode + = 5 ;
kzlen - = 5 ;
break ;
case 0xb8 :
case 0xb9 :
case 0xba :
case 0xbb :
case 0xbd :
case 0xbe :
case 0xbf :
if ( * kzstate = = KZSGETSIZE & & cli_readint32 ( kzcode ) = = 0x0fd2 ) {
kzinitlen = kzlen - 5 ;
kzdsize = op - 0xb8 ;
kzstate + + ;
op = 4 ; /* fake the register to avoid breaking out */
cli_dbgmsg ( " cli_scanpe: kriz: using #%d as size counter \n " , kzdsize ) ;
}
opsz = 4 ;
/* fall-through */
case 0x48 :
case 0x49 :
case 0x4a :
case 0x4b :
case 0x4d :
case 0x4e :
case 0x4f :
op & = 7 ;
if ( op ! = kzdptr & & op ! = kzdsize ) {
kzcode + = opsz ;
kzlen - = opsz ;
break ;
}
/* fall-through */
default :
kzcode - - ;
kzlen + + ;
kzstate + + ;
}
break ;
}
case KZSCDELTA :
if ( op = = 0xe8 & & ( uint32_t ) cli_readint32 ( kzcode ) < 0xff ) {
kzlen - = * kzcode + 4 ;
kzcode + = * kzcode + 4 ;
kzstate + + ;
} else {
* kzstate = KZSTOP ;
}
break ;
case KZSPDELTA :
if ( ( op & 0xf8 ) = = 0x58 & & ( kzdptr = op - 0x58 ) ! = 4 ) {
kzstate + + ;
cli_dbgmsg ( " cli_scanpe: kriz: using #%d as pointer \n " , kzdptr ) ;
} else {
* kzstate = KZSTOP ;
}
break ;
case KZSXORPRFX :
kzstate + + ;
if ( op = = 0x3e ) {
break ;
}
/* fall-through */
case KZSXOR :
if ( op = = 0x80 & & * kzcode = = kzdptr + 0xb0 ) {
kzxorlen = kzlen ;
kzcode + = + 6 ;
kzlen - = + 6 ;
kzstate + + ;
} else {
* kzstate = KZSTOP ;
}
break ;
case KZSDDELTA :
if ( op = = kzdptr + 0x48 )
kzstate + + ;
else
* kzstate = KZSTOP ;
break ;
case KZSLOOP :
if ( op = = kzdsize + 0x48 & & * kzcode = = 0x75 & & kzlen - ( int8_t ) kzcode [ 1 ] - 3 < = kzinitlen & & kzlen - ( int8_t ) kzcode [ 1 ] > = kzxorlen ) {
2023-01-14 18:28:39 +08:00
ret = cli_append_potentially_unwanted ( ctx , " Heuristics.W32.Kriz " ) ;
if ( ret ! = CL_SUCCESS ) {
cli_exe_info_destroy ( peinfo ) ;
return ret ;
2022-10-22 18:41:00 +08:00
}
}
cli_dbgmsg ( " cli_scanpe: kriz: loop out of bounds, corrupted sample? \n " ) ;
kzstate + + ;
}
}
}
/* W32.Magistr.A/B */
if ( SCAN_HEURISTICS & & ( DCONF & PE_CONF_MAGISTR ) & & ! peinfo - > is_dll & & ( peinfo - > nsections > 1 ) & & ( peinfo - > sections [ peinfo - > nsections - 1 ] . chr & 0x80000000 ) ) {
uint32_t rsize , vsize , dam = 0 ;
vsize = peinfo - > sections [ peinfo - > nsections - 1 ] . uvsz ;
rsize = peinfo - > sections [ peinfo - > nsections - 1 ] . rsz ;
if ( rsize < peinfo - > sections [ peinfo - > nsections - 1 ] . ursz ) {
rsize = peinfo - > sections [ peinfo - > nsections - 1 ] . ursz ;
dam = 1 ;
}
if ( vsize > = 0x612c & & rsize > = 0x612c & & ( ( vsize & 0xff ) = = 0xec ) ) {
int bw = rsize < 0x7000 ? rsize : 0x7000 ;
const char * tbuff ;
if ( ( tbuff = fmap_need_off_once ( map , peinfo - > sections [ peinfo - > nsections - 1 ] . raw + rsize - bw , 4096 ) ) ) {
if ( cli_memstr ( tbuff , 4091 , " \xe8 \x2c \x61 \x00 \x00 " , 5 ) ) {
2023-01-14 18:28:39 +08:00
ret = cli_append_potentially_unwanted ( ctx , dam ? " Heuristics.W32.Magistr.A.dam " : " Heuristics.W32.Magistr.A " ) ;
if ( ret ! = CL_SUCCESS ) {
cli_exe_info_destroy ( peinfo ) ;
return ret ;
2022-10-22 18:41:00 +08:00
}
}
}
} else if ( rsize > = 0x7000 & & vsize > = 0x7000 & & ( ( vsize & 0xff ) = = 0xed ) ) {
int bw = rsize < 0x8000 ? rsize : 0x8000 ;
const char * tbuff ;
if ( ( tbuff = fmap_need_off_once ( map , peinfo - > sections [ peinfo - > nsections - 1 ] . raw + rsize - bw , 4096 ) ) ) {
if ( cli_memstr ( tbuff , 4091 , " \xe8 \x04 \x72 \x00 \x00 " , 5 ) ) {
2023-01-14 18:28:39 +08:00
ret = cli_append_potentially_unwanted ( ctx , dam ? " Heuristics.W32.Magistr.B.dam " : " Heuristics.W32.Magistr.B " ) ;
if ( ret ! = CL_SUCCESS ) {
cli_exe_info_destroy ( peinfo ) ;
return ret ;
2022-10-22 18:41:00 +08:00
}
}
}
}
}
/* W32.Polipos.A */
// TODO Add endianness correction to SizeOfStackReserve access
while ( polipos & & ! peinfo - > is_dll & & peinfo - > nsections > 2 & & peinfo - > nsections < 13 & & peinfo - > e_lfanew < = 0x800 & & ( EC16 ( peinfo - > pe_opt . opt32 . Subsystem ) = = 2 | | EC16 ( peinfo - > pe_opt . opt32 . Subsystem ) = = 3 ) & & EC16 ( peinfo - > file_hdr . Machine ) = = 0x14c & & peinfo - > pe_opt . opt32 . SizeOfStackReserve > = 0x80000 ) {
uint32_t jump , jold , * jumps = NULL ;
const uint8_t * code ;
unsigned int xsjs = 0 ;
if ( peinfo - > sections [ 0 ] . rsz > CLI_MAX_ALLOCATION )
break ;
if ( peinfo - > sections [ 0 ] . rsz < 5 )
break ;
if ( ! ( code = fmap_need_off_once ( map , peinfo - > sections [ 0 ] . raw , peinfo - > sections [ 0 ] . rsz ) ) )
break ;
for ( i = 0 ; i < peinfo - > sections [ 0 ] . rsz - 5 ; i + + ) {
if ( ( uint8_t ) ( code [ i ] - 0xe8 ) > 1 )
continue ;
jump = cli_rawaddr ( peinfo - > sections [ 0 ] . rva + i + 5 + cli_readint32 ( & code [ i + 1 ] ) , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( err | | ! CLI_ISCONTAINED ( peinfo - > sections [ polipos ] . raw , peinfo - > sections [ polipos ] . rsz , jump , 9 ) )
continue ;
if ( xsjs % 128 = = 0 ) {
if ( xsjs = = 1280 )
break ;
if ( ! ( jumps = ( uint32_t * ) cli_realloc2 ( jumps , ( xsjs + 128 ) * sizeof ( uint32_t ) ) ) ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
}
j = 0 ;
for ( ; j < xsjs ; j + + ) {
if ( jumps [ j ] < jump )
continue ;
if ( jumps [ j ] = = jump ) {
xsjs - - ;
break ;
}
jold = jumps [ j ] ;
jumps [ j ] = jump ;
jump = jold ;
}
jumps [ j ] = jump ;
xsjs + + ;
}
if ( ! xsjs )
break ;
cli_dbgmsg ( " cli_scanpe: Polipos: Checking %d xsect jump(s) \n " , xsjs ) ;
for ( i = 0 ; i < xsjs ; i + + ) {
if ( ! ( code = fmap_need_off_once ( map , jumps [ i ] , 9 ) ) )
continue ;
if ( ( jump = cli_readint32 ( code ) ) = = 0x60ec8b55 | | ( code [ 4 ] = = 0x0ec & & ( ( jump = = 0x83ec8b55 & & code [ 6 ] = = 0x60 ) | | ( jump = = 0x81ec8b55 & & ! code [ 7 ] & & ! code [ 8 ] ) ) ) ) {
2023-01-14 18:28:39 +08:00
ret = cli_append_potentially_unwanted ( ctx , " Heuristics.W32.Polipos.A " ) ;
if ( ret ! = CL_SUCCESS ) {
free ( jumps ) ;
cli_exe_info_destroy ( peinfo ) ;
return ret ;
2022-10-22 18:41:00 +08:00
}
}
}
free ( jumps ) ;
break ;
}
/* Trojan.Swizzor.Gen */
if ( SCAN_HEURISTICS & & ( DCONF & PE_CONF_SWIZZOR ) & & peinfo - > nsections > 1 & & fsize > 64 * 1024 & & fsize < 4 * 1024 * 1024 ) {
if ( peinfo - > dirs [ 2 ] . Size ) {
struct swizz_stats * stats = cli_calloc ( 1 , sizeof ( * stats ) ) ;
unsigned int m = 1000 ;
ret = CL_CLEAN ;
if ( ! stats ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
} else {
cli_parseres_special ( EC32 ( peinfo - > dirs [ 2 ] . VirtualAddress ) , EC32 ( peinfo - > dirs [ 2 ] . VirtualAddress ) , map , peinfo , fsize , 0 , 0 , & m , stats ) ;
if ( ( ret = cli_detect_swizz ( stats ) ) = = CL_VIRUS ) {
2023-01-14 18:28:39 +08:00
ret = cli_append_potentially_unwanted ( ctx , " Heuristics.Trojan.Swizzor.Gen " ) ;
if ( ret ! = CL_SUCCESS ) {
free ( stats ) ;
cli_exe_info_destroy ( peinfo ) ;
return ret ;
2022-10-22 18:41:00 +08:00
}
}
}
}
}
/* !!!!!!!!!!!!!! PACKERS START HERE !!!!!!!!!!!!!! */
corrupted_cur = ctx - > corrupted_input ;
ctx - > corrupted_input = 2 ; /* caller will reset on return */
/* UPX, FSG, MEW support */
/* try to find the first section with physical size == 0 */
found = 0 ;
if ( DCONF & ( PE_CONF_UPX | PE_CONF_FSG | PE_CONF_MEW ) ) {
for ( i = 0 ; i < ( unsigned int ) peinfo - > nsections - 1 ; i + + ) {
if ( ! peinfo - > sections [ i ] . rsz & & peinfo - > sections [ i ] . vsz & & peinfo - > sections [ i + 1 ] . rsz & & peinfo - > sections [ i + 1 ] . vsz ) {
found = 1 ;
cli_dbgmsg ( " cli_scanpe: UPX/FSG/MEW: empty section found - assuming compression \n " ) ;
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonbool ( pe_json , " HasEmptySection " , 1 ) ;
# endif
break ;
}
}
}
/* MEW support */
if ( found & & ( DCONF & PE_CONF_MEW ) & & epsize > = 16 & & epbuff [ 0 ] = = ' \xe9 ' ) {
uint32_t fileoffset ;
const char * tbuff ;
// TODO shouldn't peinfo->ep be used here instead? ep is the file
// offset, vep is the entry point RVA
fileoffset = ( peinfo - > vep + cli_readint32 ( epbuff + 1 ) + 5 ) ;
while ( fileoffset = = 0x154 | | fileoffset = = 0x158 ) {
char * src ;
uint32_t offdiff , uselzma ;
cli_dbgmsg ( " cli_scanpe: MEW: found MEW characteristics %08X + %08X + 5 = %08X \n " ,
cli_readint32 ( epbuff + 1 ) , peinfo - > vep , cli_readint32 ( epbuff + 1 ) + peinfo - > vep + 5 ) ;
if ( ! ( tbuff = fmap_need_off_once ( map , fileoffset , 0xb0 ) ) )
break ;
if ( fileoffset = = 0x154 )
cli_dbgmsg ( " cli_scanpe: MEW: Win9x compatibility was set! \n " ) ;
else
cli_dbgmsg ( " cli_scanpe: MEW: Win9x compatibility was NOT set! \n " ) ;
offdiff = cli_readint32 ( tbuff + 1 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
if ( ( offdiff < = peinfo - > sections [ i + 1 ] . rva ) | |
( offdiff > = peinfo - > sections [ i + 1 ] . rva + peinfo - > sections [ i + 1 ] . raw - 4 ) ) {
cli_dbgmsg ( " cli_scanpe: MEW: ESI is not in proper section \n " ) ;
break ;
}
offdiff - = peinfo - > sections [ i + 1 ] . rva ;
if ( ! peinfo - > sections [ i + 1 ] . rsz ) {
cli_dbgmsg ( " cli_scanpe: MEW: mew section is empty \n " ) ;
break ;
}
ssize = peinfo - > sections [ i + 1 ] . vsz ;
dsize = peinfo - > sections [ i ] . vsz ;
/* Guard against integer overflow */
if ( ( ssize + dsize < ssize ) | | ( ssize + dsize < dsize ) ) {
cli_dbgmsg ( " cli_scanpe: MEW: section size (%08x) + diff size (%08x) exceeds max size of unsigned int (%08x) \n " , ssize , dsize , UINT32_MAX ) ;
break ;
}
/* Verify that offdiff does not exceed the ssize + sdiff */
if ( offdiff > = ssize + dsize ) {
cli_dbgmsg ( " cli_scanpe: MEW: offdiff (%08x) exceeds section size + diff size (%08x) \n " , offdiff , ssize + dsize ) ;
break ;
}
cli_dbgmsg ( " cli_scanpe: MEW: ssize %08x dsize %08x offdiff: %08x \n " , ssize , dsize , offdiff ) ;
CLI_UNPSIZELIMITS ( " cli_scanpe: MEW " , MAX ( ssize , dsize ) ) ;
CLI_UNPSIZELIMITS ( " cli_scanpe: MEW " , MAX ( ssize + dsize , peinfo - > sections [ i + 1 ] . rsz ) ) ;
if ( peinfo - > sections [ i + 1 ] . rsz < offdiff + 12 | | peinfo - > sections [ i + 1 ] . rsz > ssize ) {
cli_dbgmsg ( " cli_scanpe: MEW: Size mismatch: %08x \n " , peinfo - > sections [ i + 1 ] . rsz ) ;
break ;
}
/* allocate needed buffer */
if ( ! ( src = cli_calloc ( ssize + dsize , sizeof ( char ) ) ) ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
bytes = fmap_readn ( map , src + dsize , peinfo - > sections [ i + 1 ] . raw , peinfo - > sections [ i + 1 ] . rsz ) ;
if ( bytes ! = peinfo - > sections [ i + 1 ] . rsz ) {
cli_dbgmsg ( " cli_scanpe: MEW: Can't read %u bytes [read: %zu] \n " , peinfo - > sections [ i + 1 ] . rsz , bytes ) ;
cli_exe_info_destroy ( peinfo ) ;
free ( src ) ;
return CL_EREAD ;
}
cli_dbgmsg ( " cli_scanpe: MEW: %zu (%08zx) bytes read \n " , bytes , bytes ) ;
/* count offset to lzma proc, if lzma used, 0xe8 -> call */
if ( tbuff [ 0x7b ] = = ' \xe8 ' ) {
if ( ! CLI_ISCONTAINED ( peinfo - > sections [ 1 ] . rva , peinfo - > sections [ 1 ] . vsz , cli_readint32 ( tbuff + 0x7c ) + fileoffset + 0x80 , 4 ) ) {
cli_dbgmsg ( " cli_scanpe: MEW: lzma proc out of bounds! \n " ) ;
free ( src ) ;
break ; /* to next unpacker in chain */
}
uselzma = cli_readint32 ( tbuff + 0x7c ) - ( peinfo - > sections [ 0 ] . rva - fileoffset - 0x80 ) ;
} else {
uselzma = 0 ;
}
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " MEW " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: MEW " , ( src , 0 ) ) ;
CLI_UNPRESULTS ( " cli_scanpe: MEW " , ( unmew11 ( src , offdiff , ssize , dsize , EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , peinfo - > sections [ 0 ] . rva , uselzma , ndesc ) ) , 1 , ( src , 0 ) ) ;
break ;
}
}
// TODO Why do we bail here
if ( epsize < 168 ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_CLEAN ;
}
if ( found | | upack ) {
/* Check EP for UPX vs. FSG vs. Upack */
/* Upack 0.39 produces 2 types of executables
* 3 sections : | 2 sections ( one empty , I don ' t check found if ! upack , since it ' s in OR above ) :
* mov esi , value | pusha
* lodsd | call $ + 0x9
* push eax |
*
* Upack 1.1 / 1.2 Beta produces [ based on 2 samples ( sUx ) provided by aCaB ] :
* 2 sections
* mov esi , value
* loads
* mov edi , eax
*
* Upack unknown [ sample 02 97729 ]
* 3 sections
* mov esi , value
* push [ esi ]
* jmp
*
*/
/* upack 0.39-3s + sample 0151477*/
while ( ( ( upack & & peinfo - > nsections = = 3 ) & & /* 3 sections */
( (
epbuff [ 0 ] = = ' \xbe ' & & cli_readint32 ( epbuff + 1 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) > peinfo - > min & & /* mov esi */
epbuff [ 5 ] = = ' \xad ' & & epbuff [ 6 ] = = ' \x50 ' /* lodsd; push eax */
) | |
/* based on 0297729 sample from aCaB */
( epbuff [ 0 ] = = ' \xbe ' & & cli_readint32 ( epbuff + 1 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) > peinfo - > min & & /* mov esi */
epbuff [ 5 ] = = ' \xff ' & & epbuff [ 6 ] = = ' \x36 ' /* push [esi] */
) ) ) | |
( ( ! upack & & peinfo - > nsections = = 2 ) & & /* 2 sections */
( ( /* upack 0.39-2s */
epbuff [ 0 ] = = ' \x60 ' & & epbuff [ 1 ] = = ' \xe8 ' & & cli_readint32 ( epbuff + 2 ) = = 0x9 /* pusha; call+9 */
) | |
( /* upack 1.1/1.2, based on 2 samples */
epbuff [ 0 ] = = ' \xbe ' & & cli_readint32 ( epbuff + 1 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) < peinfo - > min & & /* mov esi */
2023-01-14 18:28:39 +08:00
cli_readint32 ( epbuff + 1 ) > EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) & &
2022-10-22 18:41:00 +08:00
epbuff [ 5 ] = = ' \xad ' & & epbuff [ 6 ] = = ' \x8b ' & & epbuff [ 7 ] = = ' \xf8 ' /* loads; mov edi, eax */
) ) ) ) {
uint32_t vma , off ;
int a , b , c ;
cli_dbgmsg ( " cli_scanpe: Upack characteristics found. \n " ) ;
a = peinfo - > sections [ 0 ] . vsz ;
b = peinfo - > sections [ 1 ] . vsz ;
if ( upack ) {
cli_dbgmsg ( " cli_scanpe: Upack: var set \n " ) ;
c = peinfo - > sections [ 2 ] . vsz ;
ssize = peinfo - > sections [ 0 ] . ursz + peinfo - > sections [ 0 ] . uraw ;
off = peinfo - > sections [ 0 ] . rva ;
vma = EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) + peinfo - > sections [ 0 ] . rva ;
} else {
cli_dbgmsg ( " cli_scanpe: Upack: var NOT set \n " ) ;
c = peinfo - > sections [ 1 ] . rva ;
ssize = peinfo - > sections [ 1 ] . uraw ;
off = 0 ;
vma = peinfo - > sections [ 1 ] . rva - peinfo - > sections [ 1 ] . uraw ;
}
dsize = a + b + c ;
CLI_UNPSIZELIMITS ( " cli_scanpe: Upack " , MAX ( MAX ( dsize , ssize ) , peinfo - > sections [ 1 ] . ursz ) ) ;
if ( ! CLI_ISCONTAINED_0_TO ( dsize , peinfo - > sections [ 1 ] . rva - off , peinfo - > sections [ 1 ] . ursz ) | | ( upack & & ! CLI_ISCONTAINED_0_TO ( dsize , peinfo - > sections [ 2 ] . rva - peinfo - > sections [ 0 ] . rva , ssize ) ) | | ssize > dsize ) {
cli_dbgmsg ( " cli_scanpe: Upack: probably malformed pe-header, skipping to next unpacker \n " ) ;
break ;
}
if ( ( dest = ( char * ) cli_calloc ( dsize , sizeof ( char ) ) ) = = NULL ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
if ( fmap_readn ( map , dest , 0 , ssize ) ! = ssize ) {
cli_dbgmsg ( " cli_scanpe: Upack: Can't read raw data of section 0 \n " ) ;
free ( dest ) ;
break ;
}
if ( upack )
memmove ( dest + peinfo - > sections [ 2 ] . rva - peinfo - > sections [ 0 ] . rva , dest , ssize ) ;
if ( fmap_readn ( map , dest + peinfo - > sections [ 1 ] . rva - off , peinfo - > sections [ 1 ] . uraw , peinfo - > sections [ 1 ] . ursz ) ! = peinfo - > sections [ 1 ] . ursz ) {
cli_dbgmsg ( " cli_scanpe: Upack: Can't read raw data of section 1 \n " ) ;
free ( dest ) ;
break ;
}
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " Upack " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: Upack " , ( dest , 0 ) ) ;
CLI_UNPRESULTS ( " cli_scanpe: Upack " , ( unupack ( upack , dest , dsize , epbuff , vma , peinfo - > ep , EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , peinfo - > sections [ 0 ] . rva , ndesc ) ) , 1 , ( dest , 0 ) ) ;
break ;
}
}
while ( found & & ( DCONF & PE_CONF_FSG ) & & epbuff [ 0 ] = = ' \x87 ' & & epbuff [ 1 ] = = ' \x25 ' ) {
const char * dst ;
uint32_t newesi , newedi , newebx , newedx ;
/* FSG v2.0 support - thanks to aCaB ! */
ssize = peinfo - > sections [ i + 1 ] . rsz ;
dsize = peinfo - > sections [ i ] . vsz ;
CLI_UNPSIZELIMITS ( " cli_scanpe: FSG " , MAX ( dsize , ssize ) ) ;
if ( ssize < = 0x19 | | dsize < = ssize ) {
cli_dbgmsg ( " cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d) \n " , ssize , dsize ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_CLEAN ;
}
newedx = cli_readint32 ( epbuff + 2 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
if ( ! CLI_ISCONTAINED ( peinfo - > sections [ i + 1 ] . rva , peinfo - > sections [ i + 1 ] . rsz , newedx , 4 ) ) {
cli_dbgmsg ( " cli_scanpe: FSG: xchg out of bounds (%x), giving up \n " , newedx ) ;
break ;
}
if ( ! peinfo - > sections [ i + 1 ] . rsz | | ! ( src = fmap_need_off_once ( map , peinfo - > sections [ i + 1 ] . raw , ssize ) ) ) {
cli_dbgmsg ( " cli_scanpe: Can't read raw data of section %d \n " , i + 1 ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_ESEEK ;
}
dst = src + newedx - peinfo - > sections [ i + 1 ] . rva ;
if ( newedx < peinfo - > sections [ i + 1 ] . rva | | ! CLI_ISCONTAINED ( src , ssize , dst , 4 ) ) {
cli_dbgmsg ( " cli_scanpe: FSG: New ESP out of bounds \n " ) ;
break ;
}
newedx = cli_readint32 ( dst ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
if ( ! CLI_ISCONTAINED ( peinfo - > sections [ i + 1 ] . rva , peinfo - > sections [ i + 1 ] . rsz , newedx , 4 ) ) {
cli_dbgmsg ( " cli_scanpe: FSG: New ESP (%x) is wrong \n " , newedx ) ;
break ;
}
dst = src + newedx - peinfo - > sections [ i + 1 ] . rva ;
if ( ! CLI_ISCONTAINED ( src , ssize , dst , 32 ) ) {
cli_dbgmsg ( " cli_scanpe: FSG: New stack out of bounds \n " ) ;
break ;
}
newedi = cli_readint32 ( dst ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
newesi = cli_readint32 ( dst + 4 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
newebx = cli_readint32 ( dst + 16 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
newedx = cli_readint32 ( dst + 20 ) ;
if ( newedi ! = peinfo - > sections [ i ] . rva ) {
cli_dbgmsg ( " cli_scanpe: FSG: Bad destination buffer (edi is %x should be %x) \n " , newedi , peinfo - > sections [ i ] . rva ) ;
break ;
}
if ( newesi < peinfo - > sections [ i + 1 ] . rva | | newesi - peinfo - > sections [ i + 1 ] . rva > = peinfo - > sections [ i + 1 ] . rsz ) {
cli_dbgmsg ( " cli_scanpe: FSG: Source buffer out of section bounds \n " ) ;
break ;
}
if ( ! CLI_ISCONTAINED ( peinfo - > sections [ i + 1 ] . rva , peinfo - > sections [ i + 1 ] . rsz , newebx , 16 ) ) {
cli_dbgmsg ( " cli_scanpe: FSG: Array of functions out of bounds \n " ) ;
break ;
}
newedx = cli_readint32 ( newebx + 12 - peinfo - > sections [ i + 1 ] . rva + src ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
cli_dbgmsg ( " cli_scanpe: FSG: found old EP @%x \n " , newedx ) ;
if ( ( dest = ( char * ) cli_calloc ( dsize , sizeof ( char ) ) ) = = NULL ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " FSG " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: FSG " , ( dest , 0 ) ) ;
CLI_UNPRESULTSFSG2 ( " cli_scanpe: FSG " , ( unfsg_200 ( newesi - peinfo - > sections [ i + 1 ] . rva + src , dest , ssize + peinfo - > sections [ i + 1 ] . rva - newesi , dsize , newedi , EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , newedx , ndesc ) ) , 1 , ( dest , 0 ) ) ;
break ;
}
while ( found & & ( DCONF & PE_CONF_FSG ) & & epbuff [ 0 ] = = ' \xbe ' & & cli_readint32 ( epbuff + 1 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) < peinfo - > min ) {
int sectcnt = 0 ;
const char * support ;
uint32_t newesi , newedi , oldep , gp , t ;
struct cli_exe_section * sections ;
/* FSG support - v. 1.33 (thx trog for the many samples) */
ssize = peinfo - > sections [ i + 1 ] . rsz ;
dsize = peinfo - > sections [ i ] . vsz ;
CLI_UNPSIZELIMITS ( " cli_scanpe: FSG " , MAX ( dsize , ssize ) ) ;
if ( ssize < = 0x19 | | dsize < = ssize ) {
cli_dbgmsg ( " cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d) \n " , ssize , dsize ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_CLEAN ;
}
if ( ! ( t = cli_rawaddr ( cli_readint32 ( epbuff + 1 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , NULL , 0 , & err , fsize , peinfo - > hdr_size ) ) & & err ) {
cli_dbgmsg ( " cli_scanpe: FSG: Support data out of padding area \n " ) ;
break ;
}
gp = peinfo - > sections [ i + 1 ] . raw - t ;
CLI_UNPSIZELIMITS ( " cli_scanpe: FSG " , gp ) ;
if ( ! ( support = fmap_need_off_once ( map , t , gp ) ) ) {
cli_dbgmsg ( " cli_scanpe: Can't read %d bytes from padding area \n " , gp ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EREAD ;
}
/* newebx = cli_readint32(support) - EC32(peinfo->pe_opt.opt32.ImageBase); Unused */
newedi = cli_readint32 ( support + 4 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ; /* 1st dest */
newesi = cli_readint32 ( support + 8 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ; /* Source */
if ( newesi < peinfo - > sections [ i + 1 ] . rva | | newesi - peinfo - > sections [ i + 1 ] . rva > = peinfo - > sections [ i + 1 ] . rsz ) {
cli_dbgmsg ( " cli_scanpe: FSG: Source buffer out of section bounds \n " ) ;
break ;
}
if ( newedi ! = peinfo - > sections [ i ] . rva ) {
cli_dbgmsg ( " cli_scanpe: FSG: Bad destination (is %x should be %x) \n " , newedi , peinfo - > sections [ i ] . rva ) ;
break ;
}
/* Counting original sections */
for ( t = 12 ; t < gp - 4 ; t + = 4 ) {
uint32_t rva = cli_readint32 ( support + t ) ;
if ( ! rva )
break ;
rva - = EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) + 1 ;
sectcnt + + ;
if ( rva % 0x1000 )
cli_dbgmsg ( " cli_scanpe: FSG: Original section %d is misaligned \n " , sectcnt ) ;
if ( rva < peinfo - > sections [ i ] . rva | | rva - peinfo - > sections [ i ] . rva > = peinfo - > sections [ i ] . vsz ) {
cli_dbgmsg ( " cli_scanpe: FSG: Original section %d is out of bounds \n " , sectcnt ) ;
break ;
}
}
if ( t > = gp - 4 | | cli_readint32 ( support + t ) ) {
break ;
}
if ( ( sections = ( struct cli_exe_section * ) cli_malloc ( ( sectcnt + 1 ) * sizeof ( struct cli_exe_section ) ) ) = = NULL ) {
cli_errmsg ( " cli_scanpe: FSG: Unable to allocate memory for sections %llu \n " , ( long long unsigned ) ( ( sectcnt + 1 ) * sizeof ( struct cli_exe_section ) ) ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
sections [ 0 ] . rva = newedi ;
for ( t = 1 ; t < = ( uint32_t ) sectcnt ; t + + )
sections [ t ] . rva = cli_readint32 ( support + 8 + t * 4 ) - 1 - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
if ( ! peinfo - > sections [ i + 1 ] . rsz | | ! ( src = fmap_need_off_once ( map , peinfo - > sections [ i + 1 ] . raw , ssize ) ) ) {
cli_dbgmsg ( " cli_scanpe: Can't read raw data of section %d \n " , i ) ;
cli_exe_info_destroy ( peinfo ) ;
free ( sections ) ;
return CL_EREAD ;
}
if ( ( dest = ( char * ) cli_calloc ( dsize , sizeof ( char ) ) ) = = NULL ) {
cli_exe_info_destroy ( peinfo ) ;
free ( sections ) ;
return CL_EMEM ;
}
oldep = peinfo - > vep + 161 + 6 + cli_readint32 ( epbuff + 163 ) ;
cli_dbgmsg ( " cli_scanpe: FSG: found old EP @%x \n " , oldep ) ;
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " FSG " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: FSG " , ( dest , sections , 0 ) ) ;
CLI_UNPRESULTSFSG1 ( " cli_scanpe: FSG " , ( unfsg_133 ( src + newesi - peinfo - > sections [ i + 1 ] . rva , dest , ssize + peinfo - > sections [ i + 1 ] . rva - newesi , dsize , sections , sectcnt , EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , oldep , ndesc ) ) , 1 , ( dest , sections , 0 ) ) ;
break ; /* were done with 1.33 */
}
while ( found & & ( DCONF & PE_CONF_FSG ) & & epbuff [ 0 ] = = ' \xbb ' & & cli_readint32 ( epbuff + 1 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) < peinfo - > min & & epbuff [ 5 ] = = ' \xbf ' & & epbuff [ 10 ] = = ' \xbe ' & & peinfo - > vep > = peinfo - > sections [ i + 1 ] . rva & & peinfo - > vep - peinfo - > sections [ i + 1 ] . rva > peinfo - > sections [ i + 1 ] . rva - 0xe0 ) {
int sectcnt = 0 ;
uint32_t gp , t = cli_rawaddr ( cli_readint32 ( epbuff + 1 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , NULL , 0 , & err , fsize , peinfo - > hdr_size ) ;
const char * support ;
uint32_t newesi = cli_readint32 ( epbuff + 11 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
uint32_t newedi = cli_readint32 ( epbuff + 6 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
uint32_t oldep = peinfo - > vep - peinfo - > sections [ i + 1 ] . rva ;
struct cli_exe_section * sections ;
/* FSG support - v. 1.31 */
ssize = peinfo - > sections [ i + 1 ] . rsz ;
dsize = peinfo - > sections [ i ] . vsz ;
if ( err ) {
cli_dbgmsg ( " cli_scanpe: FSG: Support data out of padding area \n " ) ;
break ;
}
if ( newesi < peinfo - > sections [ i + 1 ] . rva | | newesi - peinfo - > sections [ i + 1 ] . rva > = peinfo - > sections [ i + 1 ] . raw ) {
cli_dbgmsg ( " cli_scanpe: FSG: Source buffer out of section bounds \n " ) ;
break ;
}
if ( newedi ! = peinfo - > sections [ i ] . rva ) {
cli_dbgmsg ( " cli_scanpe: FSG: Bad destination (is %x should be %x) \n " , newedi , peinfo - > sections [ i ] . rva ) ;
break ;
}
CLI_UNPSIZELIMITS ( " cli_scanpe: FSG " , MAX ( dsize , ssize ) ) ;
if ( ssize < = 0x19 | | dsize < = ssize ) {
cli_dbgmsg ( " cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d) \n " , ssize , dsize ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_CLEAN ;
}
gp = peinfo - > sections [ i + 1 ] . raw - t ;
CLI_UNPSIZELIMITS ( " cli_scanpe: FSG " , gp )
if ( ! ( support = fmap_need_off_once ( map , t , gp ) ) ) {
cli_dbgmsg ( " cli_scanpe: Can't read %d bytes from padding area \n " , gp ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EREAD ;
}
/* Counting original sections */
for ( t = 0 ; t < gp - 2 ; t + = 2 ) {
uint32_t rva = support [ t ] | ( support [ t + 1 ] < < 8 ) ;
if ( rva = = 2 | | rva = = 1 )
break ;
rva = ( ( rva - 2 ) < < 12 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
sectcnt + + ;
if ( rva < peinfo - > sections [ i ] . rva | | rva - peinfo - > sections [ i ] . rva > = peinfo - > sections [ i ] . vsz ) {
cli_dbgmsg ( " cli_scanpe: FSG: Original section %d is out of bounds \n " , sectcnt ) ;
break ;
}
}
if ( t > = gp - 10 | | cli_readint32 ( support + t + 6 ) ! = 2 )
break ;
if ( ( sections = ( struct cli_exe_section * ) cli_malloc ( ( sectcnt + 1 ) * sizeof ( struct cli_exe_section ) ) ) = = NULL ) {
cli_errmsg ( " cli_scanpe: FSG: Unable to allocate memory for sections %llu \n " , ( long long unsigned ) ( ( sectcnt + 1 ) * sizeof ( struct cli_exe_section ) ) ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
sections [ 0 ] . rva = newedi ;
for ( t = 0 ; t < = ( uint32_t ) sectcnt - 1 ; t + + )
sections [ t + 1 ] . rva = ( ( ( support [ t * 2 ] | ( support [ t * 2 + 1 ] < < 8 ) ) - 2 ) < < 12 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ;
if ( ! peinfo - > sections [ i + 1 ] . rsz | | ! ( src = fmap_need_off_once ( map , peinfo - > sections [ i + 1 ] . raw , ssize ) ) ) {
cli_dbgmsg ( " cli_scanpe: FSG: Can't read raw data of section %d \n " , i ) ;
cli_exe_info_destroy ( peinfo ) ;
free ( sections ) ;
return CL_EREAD ;
}
if ( ( dest = ( char * ) cli_calloc ( dsize , sizeof ( char ) ) ) = = NULL ) {
cli_exe_info_destroy ( peinfo ) ;
free ( sections ) ;
return CL_EMEM ;
}
gp = 0xda + 6 * ( epbuff [ 16 ] = = ' \xe8 ' ) ;
oldep = peinfo - > vep + gp + 6 + cli_readint32 ( src + gp + 2 + oldep ) ;
cli_dbgmsg ( " cli_scanpe: FSG: found old EP @%x \n " , oldep ) ;
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " FSG " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: FSG " , ( dest , sections , 0 ) ) ;
CLI_UNPRESULTSFSG1 ( " cli_scanpe: FSG " , ( unfsg_133 ( src + newesi - peinfo - > sections [ i + 1 ] . rva , dest , ssize + peinfo - > sections [ i + 1 ] . rva - newesi , dsize , sections , sectcnt , EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , oldep , ndesc ) ) , 1 , ( dest , sections , 0 ) ) ;
break ; /* were done with 1.31 */
}
if ( found & & ( DCONF & PE_CONF_UPX ) ) {
ssize = peinfo - > sections [ i + 1 ] . rsz ;
dsize = peinfo - > sections [ i ] . vsz + peinfo - > sections [ i + 1 ] . vsz ;
/*
* UPX support
* we assume ( i + 1 ) is UPX1
*/
/* cli_dbgmsg("UPX: ssize %u dsize %u\n", ssize, dsize); */
CLI_UNPSIZELIMITS ( " cli_scanpe: UPX " , MAX ( dsize , ssize ) ) ;
if ( ssize < = 0x19 | | dsize < = ssize | | dsize > CLI_MAX_ALLOCATION ) {
cli_dbgmsg ( " cli_scanpe: UPX: Size mismatch or dsize too big (ssize: %d, dsize: %d) \n " , ssize , dsize ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_CLEAN ;
}
if ( ! peinfo - > sections [ i + 1 ] . rsz | | ! ( src = fmap_need_off_once ( map , peinfo - > sections [ i + 1 ] . raw , ssize ) ) ) {
cli_dbgmsg ( " cli_scanpe: UPX: Can't read raw data of section %d \n " , i + 1 ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EREAD ;
}
if ( ( dest = ( char * ) cli_calloc ( dsize + 8192 , sizeof ( char ) ) ) = = NULL ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
/* try to detect UPX code */
if ( cli_memstr ( UPX_NRV2B , 24 , epbuff + 0x69 , 13 ) | | cli_memstr ( UPX_NRV2B , 24 , epbuff + 0x69 + 8 , 13 ) ) {
cli_dbgmsg ( " cli_scanpe: UPX: Looks like a NRV2B decompression routine \n " ) ;
upxfn = upx_inflate2b ;
} else if ( cli_memstr ( UPX_NRV2D , 24 , epbuff + 0x69 , 13 ) | | cli_memstr ( UPX_NRV2D , 24 , epbuff + 0x69 + 8 , 13 ) ) {
cli_dbgmsg ( " cli_scanpe: UPX: Looks like a NRV2D decompression routine \n " ) ;
upxfn = upx_inflate2d ;
} else if ( cli_memstr ( UPX_NRV2E , 24 , epbuff + 0x69 , 13 ) | | cli_memstr ( UPX_NRV2E , 24 , epbuff + 0x69 + 8 , 13 ) ) {
cli_dbgmsg ( " cli_scanpe: UPX: Looks like a NRV2E decompression routine \n " ) ;
upxfn = upx_inflate2e ;
}
if ( upxfn ) {
int skew = cli_readint32 ( epbuff + 2 ) - EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) - peinfo - > sections [ i + 1 ] . rva ;
if ( epbuff [ 1 ] ! = ' \xbe ' | | skew < = 0 | | skew > 0xfff ) {
/* FIXME: legit skews?? */
skew = 0 ;
} else if ( ( unsigned int ) skew > ssize ) {
/* Ignore suggested skew larger than section size */
skew = 0 ;
} else {
cli_dbgmsg ( " cli_scanpe: UPX: UPX1 seems skewed by %d bytes \n " , skew ) ;
}
/* Try skewed first (skew may be zero) */
if ( upxfn ( src + skew , ssize - skew , dest , & dsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep - skew ) > = 0 ) {
upx_success = 1 ;
}
/* If skew not successful and non-zero, try no skew */
else if ( skew & & ( upxfn ( src , ssize , dest , & dsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep ) > = 0 ) ) {
upx_success = 1 ;
}
if ( upx_success )
cli_dbgmsg ( " cli_scanpe: UPX: Successfully decompressed \n " ) ;
else
cli_dbgmsg ( " cli_scanpe: UPX: Preferred decompressor failed \n " ) ;
}
if ( ! upx_success & & upxfn ! = upx_inflate2b ) {
if ( upx_inflate2b ( src , ssize , dest , & dsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep ) = = - 1 & & upx_inflate2b ( src + 0x15 , ssize - 0x15 , dest , & dsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep - 0x15 ) = = - 1 ) {
cli_dbgmsg ( " cli_scanpe: UPX: NRV2B decompressor failed \n " ) ;
} else {
upx_success = 1 ;
cli_dbgmsg ( " cli_scanpe: UPX: Successfully decompressed with NRV2B \n " ) ;
}
}
if ( ! upx_success & & upxfn ! = upx_inflate2d ) {
if ( upx_inflate2d ( src , ssize , dest , & dsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep ) = = - 1 & & upx_inflate2d ( src + 0x15 , ssize - 0x15 , dest , & dsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep - 0x15 ) = = - 1 ) {
cli_dbgmsg ( " cli_scanpe: UPX: NRV2D decompressor failed \n " ) ;
} else {
upx_success = 1 ;
cli_dbgmsg ( " cli_scanpe: UPX: Successfully decompressed with NRV2D \n " ) ;
}
}
if ( ! upx_success & & upxfn ! = upx_inflate2e ) {
if ( upx_inflate2e ( src , ssize , dest , & dsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep ) = = - 1 & & upx_inflate2e ( src + 0x15 , ssize - 0x15 , dest , & dsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep - 0x15 ) = = - 1 ) {
cli_dbgmsg ( " cli_scanpe: UPX: NRV2E decompressor failed \n " ) ;
} else {
upx_success = 1 ;
cli_dbgmsg ( " cli_scanpe: UPX: Successfully decompressed with NRV2E \n " ) ;
}
}
if ( cli_memstr ( UPX_LZMA2 , 20 , epbuff + 0x2f , 20 ) ) {
uint32_t strictdsize = cli_readint32 ( epbuff + 0x21 ) , skew = 0 ;
if ( ssize > 0x15 & & epbuff [ 0 ] = = ' \x60 ' & & epbuff [ 1 ] = = ' \xbe ' ) {
// TODO Add EC32
skew = cli_readint32 ( epbuff + 2 ) - peinfo - > sections [ i + 1 ] . rva - peinfo - > pe_opt . opt32 . ImageBase ;
if ( skew ! = 0x15 )
skew = 0 ;
}
if ( strictdsize < = dsize )
upx_success = upx_inflatelzma ( src + skew , ssize - skew , dest , & strictdsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep , 0x20003 ) > = 0 ;
} else if ( cli_memstr ( UPX_LZMA1_FIRST , 8 , epbuff + 0x39 , 8 ) & & cli_memstr ( UPX_LZMA1_SECOND , 8 , epbuff + 0x45 , 8 ) ) {
uint32_t strictdsize = cli_readint32 ( epbuff + 0x2b ) , skew = 0 ;
uint32_t properties = cli_readint32 ( epbuff + 0x41 ) ;
if ( ssize > 0x15 & & epbuff [ 0 ] = = ' \x60 ' & & epbuff [ 1 ] = = ' \xbe ' ) {
// TODO Add EC32
skew = cli_readint32 ( epbuff + 2 ) - peinfo - > sections [ i + 1 ] . rva - peinfo - > pe_opt . opt32 . ImageBase ;
if ( skew ! = 0x15 )
skew = 0 ;
}
if ( strictdsize < = dsize )
upx_success = upx_inflatelzma ( src + skew , ssize - skew , dest , & strictdsize , peinfo - > sections [ i ] . rva , peinfo - > sections [ i + 1 ] . rva , peinfo - > vep , properties ) > = 0 ;
}
if ( ! upx_success ) {
cli_dbgmsg ( " cli_scanpe: UPX: All decompressors failed \n " ) ;
free ( dest ) ;
}
}
if ( upx_success ) {
cli_exe_info_destroy ( peinfo ) ;
CLI_UNPTEMP ( " cli_scanpe: UPX/FSG " , ( dest , 0 ) ) ;
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " UPX " ) ;
# endif
if ( ( unsigned int ) write ( ndesc , dest , dsize ) ! = dsize ) {
cli_dbgmsg ( " cli_scanpe: UPX/FSG: Can't write %d bytes \n " , dsize ) ;
free ( tempfile ) ;
free ( dest ) ;
close ( ndesc ) ;
return CL_EWRITE ;
}
free ( dest ) ;
if ( lseek ( ndesc , 0 , SEEK_SET ) = = - 1 ) {
cli_dbgmsg ( " cli_scanpe: UPX/FSG: lseek() failed \n " ) ;
close ( ndesc ) ;
SHA_RESET ;
CLI_TMPUNLK ( ) ;
free ( tempfile ) ;
return CL_ESEEK ;
}
if ( ctx - > engine - > keeptmp )
cli_dbgmsg ( " cli_scanpe: UPX/FSG: Decompressed data saved in %s \n " , tempfile ) ;
cli_dbgmsg ( " ***** Scanning decompressed file ***** \n " ) ;
SHA_OFF ;
2023-01-14 18:28:39 +08:00
ret = cli_magic_scan_desc ( ndesc , tempfile , ctx , NULL , LAYER_ATTRIBUTES_NONE ) ;
if ( CL_SUCCESS ! = ret ) {
2022-10-22 18:41:00 +08:00
close ( ndesc ) ;
SHA_RESET ;
CLI_TMPUNLK ( ) ;
free ( tempfile ) ;
2023-01-14 18:28:39 +08:00
return ret ;
2022-10-22 18:41:00 +08:00
}
SHA_RESET ;
close ( ndesc ) ;
CLI_TMPUNLK ( ) ;
free ( tempfile ) ;
return ret ;
}
/* Petite */
if ( epsize < 200 ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_CLEAN ;
}
found = 2 ;
if ( epbuff [ 0 ] ! = ' \xb8 ' | | ( uint32_t ) cli_readint32 ( epbuff + 1 ) ! = peinfo - > sections [ peinfo - > nsections - 1 ] . rva + EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) ) {
if ( peinfo - > nsections < 2 | | epbuff [ 0 ] ! = ' \xb8 ' | | ( uint32_t ) cli_readint32 ( epbuff + 1 ) ! = peinfo - > sections [ peinfo - > nsections - 2 ] . rva + EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) )
found = 0 ;
else
found = 1 ;
}
if ( found & & ( DCONF & PE_CONF_PETITE ) ) {
cli_dbgmsg ( " cli_scanpe: Petite: v2.%d compression detected \n " , found ) ;
if ( cli_readint32 ( epbuff + 0x80 ) = = 0x163c988d ) {
cli_dbgmsg ( " cli_scanpe: Petite: level zero compression is not supported yet \n " ) ;
} else {
dsize = peinfo - > max - peinfo - > min ;
CLI_UNPSIZELIMITS ( " cli_scanpe: Petite " , dsize ) ;
if ( ( dest = ( char * ) cli_calloc ( dsize , sizeof ( char ) ) ) = = NULL ) {
cli_dbgmsg ( " cli_scanpe: Petite: Can't allocate %d bytes \n " , dsize ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
for ( i = 0 ; i < peinfo - > nsections ; i + + ) {
if ( peinfo - > sections [ i ] . raw ) {
unsigned int r_ret ;
if ( ! peinfo - > sections [ i ] . rsz )
goto out_no_petite ;
if ( ! CLI_ISCONTAINED ( dest , dsize ,
dest + peinfo - > sections [ i ] . rva - peinfo - > min ,
peinfo - > sections [ i ] . ursz ) )
goto out_no_petite ;
r_ret = fmap_readn ( map , dest + peinfo - > sections [ i ] . rva - peinfo - > min ,
peinfo - > sections [ i ] . raw ,
peinfo - > sections [ i ] . ursz ) ;
if ( r_ret ! = peinfo - > sections [ i ] . ursz ) {
out_no_petite :
cli_exe_info_destroy ( peinfo ) ;
free ( dest ) ;
return CL_CLEAN ;
}
}
}
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " Petite " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: Petite " , ( dest , 0 ) ) ;
CLI_UNPRESULTS ( " Petite " , ( petite_inflate2x_1to9 ( dest , peinfo - > min , peinfo - > max - peinfo - > min , peinfo - > sections , peinfo - > nsections - ( found = = 1 ? 1 : 0 ) , EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , peinfo - > vep , ndesc , found , EC32 ( peinfo - > dirs [ 2 ] . VirtualAddress ) , EC32 ( peinfo - > dirs [ 2 ] . Size ) ) ) , 0 , ( dest , 0 ) ) ;
}
}
/* PESpin 1.1 */
if ( ( DCONF & PE_CONF_PESPIN ) & & peinfo - > nsections > 1 & &
peinfo - > vep > = peinfo - > sections [ peinfo - > nsections - 1 ] . rva & &
0x3217 - 4 < = peinfo - > sections [ peinfo - > nsections - 1 ] . rva + peinfo - > sections [ peinfo - > nsections - 1 ] . rsz & &
peinfo - > vep < peinfo - > sections [ peinfo - > nsections - 1 ] . rva + peinfo - > sections [ peinfo - > nsections - 1 ] . rsz - 0x3217 - 4 & &
memcmp ( epbuff + 4 , " \xe8 \x00 \x00 \x00 \x00 \x8b \x1c \x24 \x83 \xc3 " , 10 ) = = 0 ) {
char * spinned ;
CLI_UNPSIZELIMITS ( " cli_scanpe: PEspin " , fsize ) ;
if ( ( spinned = ( char * ) cli_malloc ( fsize ) ) = = NULL ) {
cli_errmsg ( " cli_scanpe: PESping: Unable to allocate memory for spinned %lu \n " , ( unsigned long ) fsize ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
if ( fmap_readn ( map , spinned , 0 , fsize ) ! = fsize ) {
cli_dbgmsg ( " cli_scanpe: PESpin: Can't read %lu bytes \n " , ( unsigned long ) fsize ) ;
free ( spinned ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EREAD ;
}
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " PEspin " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: PESpin " , ( spinned , 0 ) ) ;
CLI_UNPRESULTS_ ( " cli_scanpe: PEspin " , SPINCASE ( ) , ( unspin ( spinned , fsize , peinfo - > sections , peinfo - > nsections - 1 , peinfo - > vep , ndesc , ctx ) ) , 0 , ( spinned , 0 ) ) ;
}
/* yC 1.3 & variants */
if ( ( DCONF & PE_CONF_YC ) & & peinfo - > nsections > 1 & &
( EC32 ( peinfo - > pe_opt . opt32 . AddressOfEntryPoint ) = = peinfo - > sections [ peinfo - > nsections - 1 ] . rva + 0x60 ) ) {
uint32_t ecx = 0 ;
int16_t offset ;
/* yC 1.3 */
if ( ! memcmp ( epbuff , " \x55 \x8B \xEC \x53 \x56 \x57 \x60 \xE8 \x00 \x00 \x00 \x00 \x5D \x81 \xED " , 15 ) & &
! memcmp ( epbuff + 0x26 , " \x8D \x3A \x8B \xF7 \x33 \xC0 \xEB \x04 \x90 \xEB \x01 \xC2 \xAC " , 13 ) & &
( ( uint8_t ) epbuff [ 0x13 ] = = 0xB9 ) & &
( ( uint16_t ) ( cli_readint16 ( epbuff + 0x18 ) ) = = 0xE981 ) & &
! memcmp ( epbuff + 0x1e , " \x8B \xD5 \x81 \xC2 " , 4 ) ) {
offset = 0 ;
if ( 0x6c - cli_readint32 ( epbuff + 0xf ) + cli_readint32 ( epbuff + 0x22 ) = = 0xC6 )
ecx = cli_readint32 ( epbuff + 0x14 ) - cli_readint32 ( epbuff + 0x1a ) ;
}
/* yC 1.3 variant */
if ( ! ecx & & ! memcmp ( epbuff , " \x55 \x8B \xEC \x83 \xEC \x40 \x53 \x56 \x57 " , 9 ) & &
! memcmp ( epbuff + 0x17 , " \xe8 \x00 \x00 \x00 \x00 \x5d \x81 \xed " , 8 ) & &
( ( uint8_t ) epbuff [ 0x23 ] = = 0xB9 ) ) {
offset = 0x10 ;
if ( 0x6c - cli_readint32 ( epbuff + 0x1f ) + cli_readint32 ( epbuff + 0x32 ) = = 0xC6 )
ecx = cli_readint32 ( epbuff + 0x24 ) - cli_readint32 ( epbuff + 0x2a ) ;
}
/* yC 1.x/modified */
if ( ! ecx & & ! memcmp ( epbuff , " \x60 \xe8 \x00 \x00 \x00 \x00 \x5d \x81 \xed " , 9 ) & &
( ( uint8_t ) epbuff [ 0xd ] = = 0xb9 ) & &
( ( uint16_t ) cli_readint16 ( epbuff + 0x12 ) = = 0xbd8d ) & &
! memcmp ( epbuff + 0x18 , " \x8b \xf7 \xac " , 3 ) ) {
offset = - 0x18 ;
if ( 0x66 - cli_readint32 ( epbuff + 0x9 ) + cli_readint32 ( epbuff + 0x14 ) = = 0xae )
ecx = cli_readint32 ( epbuff + 0xe ) ;
}
if ( ecx > 0x800 & & ecx < 0x2000 & &
! memcmp ( epbuff + 0x63 + offset , " \xaa \xe2 \xcc " , 3 ) & &
( fsize > = peinfo - > sections [ peinfo - > nsections - 1 ] . raw + 0xC6 + ecx + offset ) ) {
2023-01-14 18:28:39 +08:00
size_t num_alerts ;
2022-10-22 18:41:00 +08:00
char * spinned ;
if ( ( spinned = ( char * ) cli_malloc ( fsize ) ) = = NULL ) {
cli_errmsg ( " cli_scanpe: yC: Unable to allocate memory for spinned %lu \n " , ( unsigned long ) fsize ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
if ( fmap_readn ( map , spinned , 0 , fsize ) ! = fsize ) {
cli_dbgmsg ( " cli_scanpe: yC: Can't read %lu bytes \n " , ( unsigned long ) fsize ) ;
free ( spinned ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EREAD ;
}
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " yC " ) ;
# endif
2023-01-14 18:28:39 +08:00
// record number of alerts before unpacking and scanning
num_alerts = evidence_num_alerts ( ctx - > evidence ) ;
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
cli_dbgmsg ( " %d,%d,%d,%d \n " , peinfo - > nsections - 1 , peinfo - > e_lfanew , ecx , offset ) ;
CLI_UNPTEMP ( " cli_scanpe: yC " , ( spinned , 0 ) ) ;
CLI_UNPRESULTS ( " cli_scanpe: yC " , ( yc_decrypt ( ctx , spinned , fsize , peinfo - > sections , peinfo - > nsections - 1 , peinfo - > e_lfanew , ndesc , ecx , offset ) ) , 0 , ( spinned , 0 ) ) ;
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
// Unpacking may have added new alerts if the bounds-check failed.
// Compare number of alerts now with number of alerts before unpacking/scanning.
// If the number of alerts has increased, then bail.
//
// This preserves the intention of https://github.com/Cisco-Talos/clamav/commit/771c23099893f02f1316960fbe84f62b115a3556
// although that commit had it bailing if a match occured even in allmatch-mode, which we do not want to do.
if ( ! SCAN_ALLMATCHES & & num_alerts ! = evidence_num_alerts ( ctx - > evidence ) ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_VIRUS ;
}
2022-10-22 18:41:00 +08:00
}
}
/* WWPack */
while ( ( DCONF & PE_CONF_WWPACK ) & & peinfo - > nsections > 1 & &
peinfo - > vep = = peinfo - > sections [ peinfo - > nsections - 1 ] . rva & &
memcmp ( epbuff , " \x53 \x55 \x8b \xe8 \x33 \xdb \xeb " , 7 ) = = 0 & &
memcmp ( epbuff + 0x68 , " \xe8 \x00 \x00 \x00 \x00 \x58 \x2d \x6d \x00 \x00 \x00 \x50 \x60 \x33 \xc9 \x50 \x58 \x50 \x50 " , 19 ) = = 0 ) {
uint32_t head = peinfo - > sections [ peinfo - > nsections - 1 ] . raw ;
uint8_t * packer ;
char * src ;
ssize = 0 ;
for ( i = 0 ; ; i + + ) {
if ( peinfo - > sections [ i ] . raw < head )
head = peinfo - > sections [ i ] . raw ;
if ( i + 1 = = peinfo - > nsections )
break ;
if ( ssize < peinfo - > sections [ i ] . rva + peinfo - > sections [ i ] . vsz )
ssize = peinfo - > sections [ i ] . rva + peinfo - > sections [ i ] . vsz ;
}
if ( ! head | | ! ssize | | head > ssize )
break ;
CLI_UNPSIZELIMITS ( " cli_scanpe: WWPack " , ssize ) ;
if ( ! ( src = ( char * ) cli_calloc ( ssize , sizeof ( char ) ) ) ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
if ( fmap_readn ( map , src , 0 , head ) ! = head ) {
cli_dbgmsg ( " cli_scanpe: WWPack: Can't read %d bytes from headers \n " , head ) ;
free ( src ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EREAD ;
}
for ( i = 0 ; i < ( unsigned int ) peinfo - > nsections - 1 ; i + + ) {
if ( ! peinfo - > sections [ i ] . rsz )
continue ;
if ( ! CLI_ISCONTAINED ( src , ssize , src + peinfo - > sections [ i ] . rva , peinfo - > sections [ i ] . rsz ) )
break ;
if ( fmap_readn ( map , src + peinfo - > sections [ i ] . rva , peinfo - > sections [ i ] . raw , peinfo - > sections [ i ] . rsz ) ! = peinfo - > sections [ i ] . rsz )
break ;
}
if ( i + 1 ! = peinfo - > nsections ) {
cli_dbgmsg ( " cli_scanpe: WWpack: Probably hacked/damaged file. \n " ) ;
free ( src ) ;
break ;
}
if ( ( packer = ( uint8_t * ) cli_calloc ( peinfo - > sections [ peinfo - > nsections - 1 ] . rsz , sizeof ( char ) ) ) = = NULL ) {
free ( src ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
if ( ! peinfo - > sections [ peinfo - > nsections - 1 ] . rsz | | fmap_readn ( map , packer , peinfo - > sections [ peinfo - > nsections - 1 ] . raw , peinfo - > sections [ peinfo - > nsections - 1 ] . rsz ) ! = peinfo - > sections [ peinfo - > nsections - 1 ] . rsz ) {
cli_dbgmsg ( " cli_scanpe: WWPack: Can't read %d bytes from wwpack sect \n " , peinfo - > sections [ peinfo - > nsections - 1 ] . rsz ) ;
free ( src ) ;
free ( packer ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EREAD ;
}
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " WWPack " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: WWPack " , ( src , packer , 0 ) ) ;
CLI_UNPRESULTS ( " cli_scanpe: WWPack " , ( wwunpack ( ( uint8_t * ) src , ssize , packer , peinfo - > sections , peinfo - > nsections - 1 , peinfo - > e_lfanew , ndesc ) ) , 0 , ( src , packer , 0 ) ) ;
break ;
}
/* ASPACK support */
while ( ( DCONF & PE_CONF_ASPACK ) & &
( ( peinfo - > ep + ASPACK_EP_OFFSET_212 < fsize ) | |
( peinfo - > ep + ASPACK_EP_OFFSET_OTHER < fsize ) | |
( peinfo - > ep + ASPACK_EP_OFFSET_242 < fsize ) ) & &
( ! memcmp ( epbuff , " \x60 \xe8 \x03 \x00 \x00 \x00 \xe9 \xeb " , 8 ) ) ) {
char * src ;
aspack_version_t aspack_ver = ASPACK_VER_NONE ;
if ( epsize < 0x3bf )
break ;
if ( 0 = = memcmp ( epbuff + ASPACK_EPBUFF_OFFSET_212 , " \x68 \x00 \x00 \x00 \x00 \xc3 " , 6 ) ) {
aspack_ver = ASPACK_VER_212 ;
} else if ( 0 = = memcmp ( epbuff + ASPACK_EPBUFF_OFFSET_OTHER , " \x68 \x00 \x00 \x00 \x00 \xc3 " , 6 ) ) {
aspack_ver = ASPACK_VER_OTHER ;
} else if ( 0 = = memcmp ( epbuff + ASPACK_EPBUFF_OFFSET_242 , " \x68 \x00 \x00 \x00 \x00 \xc3 " , 6 ) ) {
aspack_ver = ASPACK_VER_242 ;
} else {
break ;
}
ssize = 0 ;
for ( i = 0 ; i < peinfo - > nsections ; i + + )
if ( ssize < peinfo - > sections [ i ] . rva + peinfo - > sections [ i ] . vsz )
ssize = peinfo - > sections [ i ] . rva + peinfo - > sections [ i ] . vsz ;
if ( ! ssize )
break ;
CLI_UNPSIZELIMITS ( " cli_scanpe: Aspack " , ssize ) ;
if ( ! ( src = ( char * ) cli_calloc ( ssize , sizeof ( char ) ) ) ) {
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
for ( i = 0 ; i < ( unsigned int ) peinfo - > nsections ; i + + ) {
if ( ! peinfo - > sections [ i ] . rsz )
continue ;
if ( ! CLI_ISCONTAINED ( src , ssize , src + peinfo - > sections [ i ] . rva , peinfo - > sections [ i ] . rsz ) )
break ;
if ( fmap_readn ( map , src + peinfo - > sections [ i ] . rva , peinfo - > sections [ i ] . raw , peinfo - > sections [ i ] . rsz ) ! = peinfo - > sections [ i ] . rsz )
break ;
}
if ( i ! = peinfo - > nsections ) {
cli_dbgmsg ( " cli_scanpe: Aspack: Probably hacked/damaged Aspack file. \n " ) ;
free ( src ) ;
break ;
}
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " Aspack " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: Aspack " , ( src , 0 ) ) ;
CLI_UNPRESULTS ( " cli_scanpe: Aspack " , ( unaspack ( ( uint8_t * ) src , ssize , peinfo - > sections , peinfo - > nsections , peinfo - > vep - 1 , EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , ndesc , aspack_ver ) ) , 1 , ( src , 0 ) ) ;
break ;
}
/* NsPack */
while ( DCONF & PE_CONF_NSPACK ) {
uint32_t eprva = peinfo - > vep ;
uint32_t start_of_stuff , rep = peinfo - > ep ;
unsigned int nowinldr ;
const char * nbuff ;
src = epbuff ;
if ( * epbuff = = ' \xe9 ' ) { /* bitched headers */
eprva = cli_readint32 ( epbuff + 1 ) + peinfo - > vep + 5 ;
if ( ! ( rep = cli_rawaddr ( eprva , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ) & & err )
break ;
if ( ! ( nbuff = fmap_need_off_once ( map , rep , 24 ) ) )
break ;
src = nbuff ;
}
if ( memcmp ( src , " \x9c \x60 \xe8 \x00 \x00 \x00 \x00 \x5d \xb8 \x07 \x00 \x00 \x00 " , 13 ) )
break ;
nowinldr = 0x54 - cli_readint32 ( src + 17 ) ;
cli_dbgmsg ( " cli_scanpe: NsPack: Found *start_of_stuff @delta-%x \n " , nowinldr ) ;
if ( ! ( nbuff = fmap_need_off_once ( map , rep - nowinldr , 4 ) ) )
break ;
start_of_stuff = rep + cli_readint32 ( nbuff ) ;
if ( ! ( nbuff = fmap_need_off_once ( map , start_of_stuff , 20 ) ) )
break ;
src = nbuff ;
if ( ! cli_readint32 ( nbuff ) ) {
start_of_stuff + = 4 ; /* FIXME: more to do */
src + = 4 ;
}
ssize = cli_readint32 ( src + 5 ) | 0xff ;
dsize = cli_readint32 ( src + 9 ) ;
CLI_UNPSIZELIMITS ( " cli_scanpe: NsPack " , MAX ( ssize , dsize ) ) ;
if ( ! ssize | | ! dsize | | dsize ! = peinfo - > sections [ 0 ] . vsz )
break ;
if ( ! ( dest = cli_malloc ( dsize ) ) ) {
cli_errmsg ( " cli_scanpe: NsPack: Unable to allocate memory for dest %u \n " , dsize ) ;
break ;
}
/* memset(dest, 0xfc, dsize); */
if ( ! ( src = fmap_need_off ( map , start_of_stuff , ssize ) ) ) {
free ( dest ) ;
break ;
}
/* memset(src, 0x00, ssize); */
eprva + = 0x27a ;
if ( ! ( rep = cli_rawaddr ( eprva , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ) & & err ) {
free ( dest ) ;
break ;
}
if ( ! ( nbuff = fmap_need_off_once ( map , rep , 5 ) ) ) {
free ( dest ) ;
break ;
}
fmap_unneed_off ( map , start_of_stuff , ssize ) ;
eprva = eprva + 5 + cli_readint32 ( nbuff + 1 ) ;
cli_dbgmsg ( " cli_scanpe: NsPack: OEP = %08x \n " , eprva ) ;
# if HAVE_JSON
if ( pe_json ! = NULL )
cli_jsonstr ( pe_json , " Packer " , " NsPack " ) ;
# endif
CLI_UNPTEMP ( " cli_scanpe: NsPack " , ( dest , 0 ) ) ;
CLI_UNPRESULTS ( " cli_scanpe: NsPack " , ( unspack ( src , dest , ctx , peinfo - > sections [ 0 ] . rva , EC32 ( peinfo - > pe_opt . opt32 . ImageBase ) , eprva , ndesc ) ) , 0 , ( dest , 0 ) ) ;
break ;
}
/* to be continued ... */
/* !!!!!!!!!!!!!! PACKERS END HERE !!!!!!!!!!!!!! */
ctx - > corrupted_input = corrupted_cur ;
/* Bytecode BC_PE_UNPACKER hook */
bc_ctx = cli_bytecode_context_alloc ( ) ;
if ( ! bc_ctx ) {
cli_errmsg ( " cli_scanpe: can't allocate memory for bc_ctx \n " ) ;
return CL_EMEM ;
}
cli_bytecode_context_setpe ( bc_ctx , & pedata , peinfo - > sections ) ;
cli_bytecode_context_setctx ( bc_ctx , ctx ) ;
ret = cli_bytecode_runhook ( ctx , ctx - > engine , bc_ctx , BC_PE_UNPACKER , map ) ;
switch ( ret ) {
case CL_VIRUS :
cli_exe_info_destroy ( peinfo ) ;
cli_bytecode_context_destroy ( bc_ctx ) ;
return CL_VIRUS ;
case CL_SUCCESS :
ndesc = cli_bytecode_context_getresult_file ( bc_ctx , & tempfile ) ;
cli_bytecode_context_destroy ( bc_ctx ) ;
if ( ndesc ! = - 1 & & tempfile ) {
CLI_UNPRESULTS ( " cli_scanpe: bytecode PE hook " , 1 , 1 , ( 0 ) ) ;
}
break ;
default :
cli_bytecode_context_destroy ( bc_ctx ) ;
}
cli_exe_info_destroy ( peinfo ) ;
# if HAVE_JSON
if ( cli_json_timeout_cycle_check ( ctx , & toval ) ! = CL_SUCCESS )
return CL_ETIMEOUT ;
# endif
2023-01-14 18:28:39 +08:00
return CL_SUCCESS ;
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
cl_error_t cli_pe_targetinfo ( cli_ctx * ctx , struct cli_exe_info * peinfo )
2022-10-22 18:41:00 +08:00
{
return cli_peheader ( ctx - > fmap , peinfo , CLI_PEHEADER_OPT_EXTRACT_VINFO , NULL ) ;
}
/** Parse the PE header and, if successful, populate peinfo
*
* @ param map The fmap_t backing the file being scanned
* @ param peinfo A structure to populate with info from the PE header . This
* MUST be initialized via cli_exe_info_init prior to calling
* @ param opts A bitfield indicating various options related to PE header
* parsing . The options are ( prefixed with CLI_PEHEADER_OPT_ ) :
* - NONE - Do default parsing
* - COLLECT_JSON - Populate ctx ' s json obj with PE header
* info
* - DBG_PRINT_INFO - Print debug information about the
* PE file . Right now , cli_peheader is
* called multiple times for a given PE ,
* so you don ' t want to print out the
* same info each time .
* - EXTRACT_VINFO - Parse the PEs VERSION_INFO metadata
* and store it in peinfo - > vinfo
* - STRICT_ON_PE_ERRORS - If specified , some cases that
* might be considered a broken
* executable cause RET_BROKEN_PE
* to be returned , but otherwise
* these will be tolerated .
* - REMOVE_MISSING_SECTIONS - If a section exists outside of the
* file , remove it from
* peinfo - > sections . Otherwise , the
* rsz is just set to 0 for it .
2023-01-14 18:28:39 +08:00
* @ param ctx The overarching cli_ctx . This is required with certain opts , but
2022-10-22 18:41:00 +08:00
* optional otherwise .
2023-01-14 18:28:39 +08:00
* @ return If the PE header is parsed successfully , CL_SUCCESS is returned .
* If it seems like the PE is broken , CL_EFORMAT is returned .
* Otherwise , one of the other error codes is returned .
2022-10-22 18:41:00 +08:00
* The caller MUST destroy peinfo , regardless of what this function
* returns .
*
* TODO What constitutes a " broken PE " seems somewhat arbitrary in places .
* I think a PE should only be considered broken if it will not run on
* any version of Windows . We should invest more time to ensure that our
* broken PE detection more closely aligns with this .
*
* TODO Simplify and get rid of CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS if
* possible . We should either fail always or ignore always , IMO .
*
* TODO Simplify and get rid of CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS if
* possible . I don ' t think it makes sense to have pieces of the code work
* off of incomplete representations of the sections ( for instance , I wonder
* if this makes any of the bytecode APIs return unexpected values ) . This
* appears to have been implemented to prevent ClamAV from crashing , though ,
* ( bb11155 ) so we need to ensure the underlying issues are addressed .
*
* TODO Consolidate when information about the PE is printed ( after successful
* PE parsing ) . This will allow us to simplify the code . Some fail cases ,
* then , will cause PE info to not be printed at all , but I think this is
* acceptable . The debug messages generated in the fail cases should point to
* what happened , and that ' s enough to track down the cause of any issues .
*
* TODO Same as above but with JSON creation
*/
2023-01-14 18:28:39 +08:00
cl_error_t cli_peheader ( fmap_t * map , struct cli_exe_info * peinfo , uint32_t opts , cli_ctx * ctx )
2022-10-22 18:41:00 +08:00
{
2023-01-14 18:28:39 +08:00
cl_error_t ret = CL_ERROR ;
2022-10-22 18:41:00 +08:00
uint16_t e_magic ; /* DOS signature ("MZ") */
const char * archtype = NULL , * subsystem = NULL ;
time_t timestamp ;
char timestr [ 32 ] ;
uint32_t data_dirs_size ;
uint16_t opt_hdr_size ;
uint32_t stored_opt_hdr_size ;
struct pe_image_file_hdr * file_hdr ;
struct pe_image_optional_hdr32 * opt32 ;
struct pe_image_optional_hdr64 * opt64 ;
struct pe_image_section_hdr * section_hdrs = NULL ;
size_t i , j , section_pe_idx ;
unsigned int err ;
uint32_t salign , falign ;
size_t fsize ;
ssize_t at ;
uint32_t is_dll = 0 ;
uint32_t is_exe = 0 ;
int native = 0 ;
size_t read ;
# if HAVE_JSON
int toval = 0 ;
struct json_object * pe_json = NULL ;
char jsonbuf [ 128 ] ;
# endif
if ( ctx = = NULL & &
( opts & CLI_PEHEADER_OPT_COLLECT_JSON | |
opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) ) {
cli_errmsg ( " cli_peheader: ctx can't be NULL for options specified \n " ) ;
goto done ;
}
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
pe_json = get_pe_property ( ctx ) ;
}
# endif
fsize = map - > len - peinfo - > offset ;
if ( fmap_readn ( map , & e_magic , peinfo - > offset , sizeof ( e_magic ) ) ! = sizeof ( e_magic ) ) {
cli_dbgmsg ( " cli_peheader: Can't read DOS signature \n " ) ;
goto done ;
}
if ( EC16 ( e_magic ) ! = PE_IMAGE_DOS_SIGNATURE & & EC16 ( e_magic ) ! = PE_IMAGE_DOS_SIGNATURE_OLD ) {
cli_dbgmsg ( " cli_peheader: Invalid DOS signature \n " ) ;
goto done ;
}
if ( fmap_readn ( map , & ( peinfo - > e_lfanew ) , peinfo - > offset + 58 + sizeof ( e_magic ) , sizeof ( peinfo - > e_lfanew ) ) ! = sizeof ( peinfo - > e_lfanew ) ) {
/* truncated header? */
cli_dbgmsg ( " cli_peheader: Unable to read e_lfanew - truncated header? \n " ) ;
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
peinfo - > e_lfanew = EC32 ( peinfo - > e_lfanew ) ;
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) {
cli_dbgmsg ( " e_lfanew == %d \n " , peinfo - > e_lfanew ) ;
}
if ( ! peinfo - > e_lfanew ) {
cli_dbgmsg ( " cli_peheader: Not a PE file - e_lfanew == 0 \n " ) ;
goto done ;
}
if ( fmap_readn ( map , & ( peinfo - > file_hdr ) , peinfo - > offset + peinfo - > e_lfanew , sizeof ( struct pe_image_file_hdr ) ) ! = sizeof ( struct pe_image_file_hdr ) ) {
/* bad information in e_lfanew - probably not a PE file */
cli_dbgmsg ( " cli_peheader: Can't read file header \n " ) ;
goto done ;
}
file_hdr = & ( peinfo - > file_hdr ) ;
if ( EC32 ( file_hdr - > Magic ) ! = PE_IMAGE_NT_SIGNATURE ) {
cli_dbgmsg ( " cli_peheader: Invalid PE signature (probably NE file) \n " ) ;
goto done ;
}
if ( EC16 ( file_hdr - > Characteristics ) & 0x2000 ) {
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON )
cli_jsonstr ( pe_json , " Type " , " DLL " ) ;
# endif
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) {
cli_dbgmsg ( " File type: DLL \n " ) ;
}
is_dll = 1 ;
} else if ( EC16 ( file_hdr - > Characteristics ) & 0x0002 ) {
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON )
cli_jsonstr ( pe_json , " Type " , " EXE " ) ;
# endif
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) {
cli_dbgmsg ( " File type: Executable \n " ) ;
}
is_exe = 1 ;
}
if ( ! is_dll & & ! is_exe ) {
cli_dbgmsg ( " cli_peheader: Assumption Violated: PE is not a DLL or EXE \n " ) ;
// TODO Don't continue if not an exe or dll?
}
peinfo - > is_dll = is_dll ;
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO | |
opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
switch ( EC16 ( file_hdr - > Machine ) ) {
case 0x0 :
archtype = " Unknown " ;
break ;
case 0x1 :
// New as of Windows 10, version 1607 and Windows Server 2016
archtype = " Target Host " ;
break ;
case 0x14c :
archtype = " 80386 " ;
break ;
case 0x14d :
archtype = " 80486 " ;
break ;
case 0x14e :
archtype = " 80586 " ;
break ;
case 0x160 :
archtype = " R3000 MIPS BE " ;
break ;
case 0x162 :
archtype = " R3000 MIPS LE " ;
break ;
case 0x166 :
archtype = " R4000 MIPS LE " ;
break ;
case 0x168 :
archtype = " R10000 MIPS LE " ;
break ;
case 0x169 :
archtype = " WCE MIPS LE " ;
break ;
case 0x184 :
archtype = " DEC Alpha AXP " ;
break ;
case 0x1a2 :
archtype = " Hitachi SH3 LE " ;
break ;
case 0x1a3 :
archtype = " Hitachi SH3-DSP " ;
break ;
case 0x1a4 :
archtype = " Hitachi SH3-E LE " ;
break ;
case 0x1a6 :
archtype = " Hitachi SH4 LE " ;
break ;
case 0x1a8 :
archtype = " Hitachi SH5 " ;
break ;
case 0x1c0 :
archtype = " ARM LE " ;
break ;
case 0x1c2 :
archtype = " ARM Thumb/Thumb-2 LE " ;
break ;
case 0x1c4 :
archtype = " ARM Thumb-2 LE " ;
break ;
case 0x1d3 :
archtype = " AM33 " ;
break ;
case 0x1f0 :
archtype = " PowerPC LE " ;
break ;
case 0x1f1 :
archtype = " PowerPC FP " ;
break ;
case 0x200 :
archtype = " IA64 " ;
break ;
case 0x266 :
archtype = " MIPS16 " ;
break ;
case 0x268 :
archtype = " M68k " ;
break ;
case 0x284 :
archtype = " DEC Alpha AXP 64bit " ;
break ;
case 0x366 :
archtype = " MIPS+FPU " ;
break ;
case 0x466 :
archtype = " MIPS16+FPU " ;
break ;
case 0x520 :
archtype = " Infineon TriCore " ;
break ;
case 0xcef :
archtype = " CEF " ;
break ;
case 0xebc :
archtype = " EFI Byte Code " ;
break ;
case 0x8664 :
archtype = " AMD64 " ;
break ;
case 0x9041 :
archtype = " M32R " ;
break ;
case 0xaa64 :
archtype = " ARM64 LE " ;
break ;
case 0xc0ee :
archtype = " CEE " ;
break ;
default :
archtype = " Unknown " ;
}
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO )
cli_dbgmsg ( " Machine type: %s \n " , archtype ) ;
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON )
cli_jsonstr ( pe_json , " ArchType " , archtype ) ;
# endif
}
peinfo - > nsections = EC16 ( file_hdr - > NumberOfSections ) ;
if ( peinfo - > nsections = = 0 ) {
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
pe_add_heuristic_property ( ctx , " BadNumberOfSections " ) ;
}
# endif
// TODO Investigate how corrupted_input is set and whether this
// check is needed
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO & &
! ctx - > corrupted_input ) {
if ( peinfo - > nsections = = 0 ) {
cli_dbgmsg ( " cli_peheader: Invalid NumberOfSections (0) \n " ) ;
}
}
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
timestamp = ( time_t ) EC32 ( file_hdr - > TimeDateStamp ) ;
opt_hdr_size = EC16 ( file_hdr - > SizeOfOptionalHeader ) ;
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) {
cli_dbgmsg ( " NumberOfSections: %d \n " , peinfo - > nsections ) ;
cli_dbgmsg ( " TimeDateStamp: %s " , cli_ctime ( & timestamp , timestr , sizeof ( timestr ) ) ) ;
cli_dbgmsg ( " SizeOfOptionalHeader: 0x%x \n " , opt_hdr_size ) ;
}
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
cli_jsonint ( pe_json , " NumberOfSections " , peinfo - > nsections ) ;
/* NOTE: the TimeDateStamp value will look like "Wed Dec 31 19:00:00 1969\n" */
cli_jsonstr ( pe_json , " TimeDateStamp " , cli_ctime ( & timestamp , timestr , sizeof ( timestr ) ) ) ;
cli_jsonint ( pe_json , " SizeOfOptionalHeader " , opt_hdr_size ) ;
}
# endif
// Ensure there are enough bytes to cover the full optional header,
// not including the data directory entries (which aren't all gauranteed
// to be there)
if ( opt_hdr_size < sizeof ( struct pe_image_optional_hdr32 ) ) {
cli_dbgmsg ( " cli_peheader: SizeOfOptionalHeader too small \n " ) ;
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
pe_add_heuristic_property ( ctx , " BadOptionalHeaderSize " ) ;
}
# endif
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
at = peinfo - > offset + peinfo - > e_lfanew + sizeof ( struct pe_image_file_hdr ) ;
if ( fmap_readn ( map , & ( peinfo - > pe_opt . opt32 ) , at , sizeof ( struct pe_image_optional_hdr32 ) ) ! = sizeof ( struct pe_image_optional_hdr32 ) ) {
cli_dbgmsg ( " cli_peheader: Can't read optional file header \n " ) ;
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
stored_opt_hdr_size = sizeof ( struct pe_image_optional_hdr32 ) ;
at + = stored_opt_hdr_size ;
opt32 = & ( peinfo - > pe_opt . opt32 ) ;
if ( EC16 ( opt32 - > Magic ) = = PE32P_SIGNATURE ) { /* PE+ */
// The PE32+ optional header is bigger by 16 bytes, so map in the
// additional bytes here
if ( opt_hdr_size < sizeof ( struct pe_image_optional_hdr64 ) ) {
cli_dbgmsg ( " cli_peheader: Incorrect SizeOfOptionalHeader for PE32+ \n " ) ;
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
pe_add_heuristic_property ( ctx , " BadOptionalHeaderSizePE32Plus " ) ;
}
# endif
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
if ( fmap_readn ( map , ( void * ) ( ( ( size_t ) & ( peinfo - > pe_opt . opt64 ) ) + sizeof ( struct pe_image_optional_hdr32 ) ) , at , OPT_HDR_SIZE_DIFF ) ! = OPT_HDR_SIZE_DIFF ) {
cli_dbgmsg ( " cli_peheader: Can't read additional optional file header bytes \n " ) ;
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
stored_opt_hdr_size + = OPT_HDR_SIZE_DIFF ;
at + = OPT_HDR_SIZE_DIFF ;
peinfo - > is_pe32plus = 1 ;
opt64 = & ( peinfo - > pe_opt . opt64 ) ;
peinfo - > vep = EC32 ( opt64 - > AddressOfEntryPoint ) ;
peinfo - > hdr_size = EC32 ( opt64 - > SizeOfHeaders ) ;
peinfo - > ndatadirs = EC32 ( opt64 - > NumberOfRvaAndSizes ) ;
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) {
cli_dbgmsg ( " File format: PE32+ \n " ) ;
cli_dbgmsg ( " MajorLinkerVersion: %d \n " , opt64 - > MajorLinkerVersion ) ;
cli_dbgmsg ( " MinorLinkerVersion: %d \n " , opt64 - > MinorLinkerVersion ) ;
cli_dbgmsg ( " SizeOfCode: 0x%x \n " , EC32 ( opt64 - > SizeOfCode ) ) ;
cli_dbgmsg ( " SizeOfInitializedData: 0x%x \n " , EC32 ( opt64 - > SizeOfInitializedData ) ) ;
cli_dbgmsg ( " SizeOfUninitializedData: 0x%x \n " , EC32 ( opt64 - > SizeOfUninitializedData ) ) ;
cli_dbgmsg ( " AddressOfEntryPoint: 0x%x \n " , peinfo - > vep ) ;
cli_dbgmsg ( " BaseOfCode: 0x%x \n " , EC32 ( opt64 - > BaseOfCode ) ) ;
cli_dbgmsg ( " SectionAlignment: 0x%x \n " , EC32 ( opt64 - > SectionAlignment ) ) ;
cli_dbgmsg ( " FileAlignment: 0x%x \n " , EC32 ( opt64 - > FileAlignment ) ) ;
cli_dbgmsg ( " MajorSubsystemVersion: %d \n " , EC16 ( opt64 - > MajorSubsystemVersion ) ) ;
cli_dbgmsg ( " MinorSubsystemVersion: %d \n " , EC16 ( opt64 - > MinorSubsystemVersion ) ) ;
cli_dbgmsg ( " SizeOfImage: 0x%x \n " , EC32 ( opt64 - > SizeOfImage ) ) ;
cli_dbgmsg ( " SizeOfHeaders: 0x%x \n " , peinfo - > hdr_size ) ;
cli_dbgmsg ( " NumberOfRvaAndSizes: %u \n " , peinfo - > ndatadirs ) ;
}
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
cli_jsonint ( pe_json , " MajorLinkerVersion " , opt64 - > MajorLinkerVersion ) ;
cli_jsonint ( pe_json , " MinorLinkerVersion " , opt64 - > MinorLinkerVersion ) ;
cli_jsonint ( pe_json , " SizeOfCode " , EC32 ( opt64 - > SizeOfCode ) ) ;
cli_jsonint ( pe_json , " SizeOfInitializedData " , EC32 ( opt64 - > SizeOfInitializedData ) ) ;
cli_jsonint ( pe_json , " SizeOfUninitializedData " , EC32 ( opt64 - > SizeOfUninitializedData ) ) ;
cli_jsonint ( pe_json , " NumberOfRvaAndSizes " , EC32 ( opt64 - > NumberOfRvaAndSizes ) ) ;
cli_jsonint ( pe_json , " MajorSubsystemVersion " , EC16 ( opt64 - > MajorSubsystemVersion ) ) ;
cli_jsonint ( pe_json , " MinorSubsystemVersion " , EC16 ( opt64 - > MinorSubsystemVersion ) ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , peinfo - > vep ) ;
cli_jsonstr ( pe_json , " EntryPoint " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , EC32 ( opt64 - > BaseOfCode ) ) ;
cli_jsonstr ( pe_json , " BaseOfCode " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , EC32 ( opt64 - > SectionAlignment ) ) ;
cli_jsonstr ( pe_json , " SectionAlignment " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , EC32 ( opt64 - > FileAlignment ) ) ;
cli_jsonstr ( pe_json , " FileAlignment " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , EC32 ( opt64 - > SizeOfImage ) ) ;
cli_jsonstr ( pe_json , " SizeOfImage " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , peinfo - > hdr_size ) ;
cli_jsonstr ( pe_json , " SizeOfHeaders " , jsonbuf ) ;
}
# endif
} else { /* PE */
peinfo - > is_pe32plus = 0 ;
peinfo - > vep = EC32 ( opt32 - > AddressOfEntryPoint ) ;
peinfo - > hdr_size = EC32 ( opt32 - > SizeOfHeaders ) ;
peinfo - > ndatadirs = EC32 ( opt32 - > NumberOfRvaAndSizes ) ;
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) {
cli_dbgmsg ( " File format: PE \n " ) ;
cli_dbgmsg ( " MajorLinkerVersion: %d \n " , opt32 - > MajorLinkerVersion ) ;
cli_dbgmsg ( " MinorLinkerVersion: %d \n " , opt32 - > MinorLinkerVersion ) ;
cli_dbgmsg ( " SizeOfCode: 0x%x \n " , EC32 ( opt32 - > SizeOfCode ) ) ;
cli_dbgmsg ( " SizeOfInitializedData: 0x%x \n " , EC32 ( opt32 - > SizeOfInitializedData ) ) ;
cli_dbgmsg ( " SizeOfUninitializedData: 0x%x \n " , EC32 ( opt32 - > SizeOfUninitializedData ) ) ;
cli_dbgmsg ( " AddressOfEntryPoint: 0x%x \n " , peinfo - > vep ) ;
cli_dbgmsg ( " BaseOfCode: 0x%x \n " , EC32 ( opt32 - > BaseOfCode ) ) ;
cli_dbgmsg ( " SectionAlignment: 0x%x \n " , EC32 ( opt32 - > SectionAlignment ) ) ;
cli_dbgmsg ( " FileAlignment: 0x%x \n " , EC32 ( opt32 - > FileAlignment ) ) ;
cli_dbgmsg ( " MajorSubsystemVersion: %d \n " , EC16 ( opt32 - > MajorSubsystemVersion ) ) ;
cli_dbgmsg ( " MinorSubsystemVersion: %d \n " , EC16 ( opt32 - > MinorSubsystemVersion ) ) ;
cli_dbgmsg ( " SizeOfImage: 0x%x \n " , EC32 ( opt32 - > SizeOfImage ) ) ;
cli_dbgmsg ( " SizeOfHeaders: 0x%x \n " , peinfo - > hdr_size ) ;
cli_dbgmsg ( " NumberOfRvaAndSizes: %u \n " , peinfo - > ndatadirs ) ;
}
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
cli_jsonint ( pe_json , " MajorLinkerVersion " , opt32 - > MajorLinkerVersion ) ;
cli_jsonint ( pe_json , " MinorLinkerVersion " , opt32 - > MinorLinkerVersion ) ;
cli_jsonint ( pe_json , " SizeOfCode " , EC32 ( opt32 - > SizeOfCode ) ) ;
cli_jsonint ( pe_json , " SizeOfInitializedData " , EC32 ( opt32 - > SizeOfInitializedData ) ) ;
cli_jsonint ( pe_json , " SizeOfUninitializedData " , EC32 ( opt32 - > SizeOfUninitializedData ) ) ;
cli_jsonint ( pe_json , " NumberOfRvaAndSizes " , EC32 ( opt32 - > NumberOfRvaAndSizes ) ) ;
cli_jsonint ( pe_json , " MajorSubsystemVersion " , EC16 ( opt32 - > MajorSubsystemVersion ) ) ;
cli_jsonint ( pe_json , " MinorSubsystemVersion " , EC16 ( opt32 - > MinorSubsystemVersion ) ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , peinfo - > vep ) ;
cli_jsonstr ( pe_json , " EntryPoint " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , EC32 ( opt32 - > BaseOfCode ) ) ;
cli_jsonstr ( pe_json , " BaseOfCode " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , EC32 ( opt32 - > SectionAlignment ) ) ;
cli_jsonstr ( pe_json , " SectionAlignment " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , EC32 ( opt32 - > FileAlignment ) ) ;
cli_jsonstr ( pe_json , " FileAlignment " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , EC32 ( opt32 - > SizeOfImage ) ) ;
cli_jsonstr ( pe_json , " SizeOfImage " , jsonbuf ) ;
snprintf ( jsonbuf , sizeof ( jsonbuf ) , " 0x%x " , peinfo - > hdr_size ) ;
cli_jsonstr ( pe_json , " SizeOfHeaders " , jsonbuf ) ;
}
# endif
}
salign = ( peinfo - > is_pe32plus ) ? EC32 ( opt64 - > SectionAlignment ) : EC32 ( opt32 - > SectionAlignment ) ;
falign = ( peinfo - > is_pe32plus ) ? EC32 ( opt64 - > FileAlignment ) : EC32 ( opt32 - > FileAlignment ) ;
switch ( peinfo - > is_pe32plus ? EC16 ( opt64 - > Subsystem ) : EC16 ( opt32 - > Subsystem ) ) {
case 0 :
subsystem = " Unknown " ;
break ;
case 1 :
subsystem = " Native (svc) " ;
native = 1 ;
break ;
case 2 :
subsystem = " Win32 GUI " ;
break ;
case 3 :
subsystem = " Win32 console " ;
break ;
case 5 :
subsystem = " OS/2 console " ;
break ;
case 7 :
subsystem = " POSIX console " ;
break ;
case 8 :
subsystem = " Native Win9x driver " ;
break ;
case 9 :
subsystem = " WinCE GUI " ;
break ;
case 10 :
subsystem = " EFI application " ;
break ;
case 11 :
subsystem = " EFI driver " ;
break ;
case 12 :
subsystem = " EFI runtime driver " ;
break ;
case 13 :
subsystem = " EFI ROM image " ;
break ;
case 14 :
subsystem = " Xbox " ;
break ;
case 16 :
subsystem = " Boot application " ;
break ;
default :
subsystem = " Unknown " ;
}
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) {
cli_dbgmsg ( " Subsystem: %s \n " , subsystem ) ;
cli_dbgmsg ( " ------------------------------------ \n " ) ;
}
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON )
cli_jsonstr ( pe_json , " Subsystem " , subsystem ) ;
# endif
if ( ! native & & ( ! salign | | ( salign % 0x1000 ) ) ) {
cli_dbgmsg ( " cli_peheader: Bad section alignment \n " ) ;
if ( opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS ) {
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
}
if ( ! native & & ( ! falign | | ( falign % 0x200 ) ) ) {
cli_dbgmsg ( " cli_peheader: Bad file alignment \n " ) ;
if ( opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS ) {
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
}
// Map in the optional header data directories. The spec defines 16
// directory entries, but NumberOfRvaAndSizes can be less than that
// and the Windows loader will pretend that the data directory does
// not exist. NumberOfRvaAndSizes can be larger than that too, which
// the Windows loader is OK with. To populate peinfo->dirs, we will
// copy in as many data dirs are specified but for a max of 16 (and
// adjust peinfo->ndatadirs accordingly)
if ( peinfo - > ndatadirs > 0x10 ) {
cli_dbgmsg ( " cli_peheader: Encountered NumberOfRvaAndSizes > 16 (suspicious) \n " ) ;
}
// In the case where we won't fully populate dirs with file data,
// ensure that the underlying memory is zero so that existing code
// can interact with peinfo->dirs without using peinfo->ndatadirs
if ( peinfo - > ndatadirs < sizeof ( peinfo - > dirs ) / sizeof ( peinfo - > dirs [ 0 ] ) ) {
memset ( & ( peinfo - > dirs ) , ' \0 ' , sizeof ( peinfo - > dirs ) ) ;
}
peinfo - > ndatadirs = MIN ( peinfo - > ndatadirs , sizeof ( peinfo - > dirs ) / sizeof ( peinfo - > dirs [ 0 ] ) ) ;
data_dirs_size = sizeof ( struct pe_image_data_dir ) * peinfo - > ndatadirs ;
if ( opt_hdr_size < ( stored_opt_hdr_size + data_dirs_size ) ) {
cli_dbgmsg ( " cli_peheader: SizeOfOptionalHeader too small (doesn't include data dir size) \n " ) ;
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
read = fmap_readn ( map , peinfo - > dirs , at , data_dirs_size ) ;
if ( ( read = = ( size_t ) - 1 ) | | ( read ! = data_dirs_size ) ) {
cli_dbgmsg ( " cli_peheader: Can't read optional file header data dirs \n " ) ;
goto done ;
}
at + = data_dirs_size ;
if ( opt_hdr_size ! = ( stored_opt_hdr_size + data_dirs_size ) ) {
/* Seek to the end of the long header */
cli_dbgmsg ( " cli_peheader: Encountered case where SizeOfOptionalHeader appears bigger than required \n " ) ;
at + = opt_hdr_size - ( stored_opt_hdr_size + data_dirs_size ) ;
}
// TODO This level of processing might not be needed in all cases
// Sanity checks
// TODO Also check that salign >= falign
if ( peinfo - > hdr_size ! = PESALIGN ( peinfo - > hdr_size , salign ) ) {
cli_dbgmsg ( " cli_peheader: SizeOfHeader is not aligned to the SectionAlignment \n " ) ;
}
if ( peinfo - > hdr_size ! = PESALIGN ( peinfo - > hdr_size , falign ) ) {
cli_dbgmsg ( " cli_peheader: SizeOfHeader is not aligned to the FileAlignment \n " ) ;
}
// TODO Why align here? -- /* Aligned headers virtual size */
// hdr_size should already be rounded up
// to a multiple of the file alignment.
// TODO in cli_checkpe_fp this aligned to falign, elsewhere it aligned to salign
peinfo - > hdr_size = PESALIGN ( peinfo - > hdr_size , salign ) ;
peinfo - > sections = ( struct cli_exe_section * ) cli_calloc ( peinfo - > nsections , sizeof ( struct cli_exe_section ) ) ;
if ( ! peinfo - > sections ) {
cli_dbgmsg ( " cli_peheader: Can't allocate memory for section headers \n " ) ;
goto done ;
}
section_hdrs = ( struct pe_image_section_hdr * ) cli_calloc ( peinfo - > nsections , sizeof ( struct pe_image_section_hdr ) ) ;
if ( ! section_hdrs ) {
cli_dbgmsg ( " cli_peheader: Can't allocate memory for section headers \n " ) ;
goto done ;
}
read = fmap_readn ( map , section_hdrs , at , peinfo - > nsections * sizeof ( struct pe_image_section_hdr ) ) ;
if ( ( read = = ( size_t ) - 1 ) | | ( read ! = peinfo - > nsections * sizeof ( struct pe_image_section_hdr ) ) ) {
cli_dbgmsg ( " cli_peheader: Can't read section header - possibly broken PE file \n " ) ;
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
at + = sizeof ( struct pe_image_section_hdr ) * peinfo - > nsections ;
// TODO Verify that this performs correctly
// TODO I'm not sure why this is necessary since the specification says
// that PointerToRawData is expected to be a multiple of the file
// alignment. Should we report this is as a PE with an error?
for ( i = 0 ; falign ! = 0x200 & & i < ( size_t ) peinfo - > nsections ; i + + ) {
/* file alignment fallback mode - blah */
if ( falign & & section_hdrs [ i ] . SizeOfRawData & & EC32 ( section_hdrs [ i ] . PointerToRawData ) % falign & & ! ( EC32 ( section_hdrs [ i ] . PointerToRawData ) % 0x200 ) ) {
cli_dbgmsg ( " cli_peheader: Encountered section with unexpected alignment - triggering fallback mode \n " ) ;
falign = 0x200 ;
}
}
fsize = ( map - > len - peinfo - > offset ) ;
// TODO Why do we fix up these alignments? This shouldn't be needed?
for ( i = 0 , section_pe_idx = 0 ; i < peinfo - > nsections ; i + + , section_pe_idx + + ) {
struct cli_exe_section * section = & ( peinfo - > sections [ i ] ) ;
struct pe_image_section_hdr * section_hdr = & ( section_hdrs [ i ] ) ;
char sname [ 9 ] ;
// TODO I don't see any documentation that says VirtualAddress and VirtualSize must be aligned
section - > rva = PEALIGN ( EC32 ( section_hdr - > VirtualAddress ) , salign ) ;
section - > vsz = PESALIGN ( EC32 ( section_hdr - > VirtualSize ) , salign ) ;
section - > raw = PEALIGN ( EC32 ( section_hdr - > PointerToRawData ) , falign ) ;
section - > rsz = PESALIGN ( EC32 ( section_hdr - > SizeOfRawData ) , falign ) ;
section - > chr = EC32 ( section_hdr - > Characteristics ) ;
section - > urva = EC32 ( section_hdr - > VirtualAddress ) ; /* Just in case */
section - > uvsz = EC32 ( section_hdr - > VirtualSize ) ;
section - > uraw = EC32 ( section_hdr - > PointerToRawData ) ;
section - > ursz = EC32 ( section_hdr - > SizeOfRawData ) ;
/* First, if a section exists totally outside of a file, remove the
* section from the list or zero out it ' s size . */
if ( section - > rsz ) { /* Don't bother with virtual only sections */
if ( section - > raw > = fsize | | section - > uraw > = fsize ) {
cli_dbgmsg ( " cli_peheader: Broken PE file - Section %zu starts or exists beyond the end of file (Offset@ %lu, Total filesize %lu) \n " , section_pe_idx , ( unsigned long ) section - > raw , ( unsigned long ) fsize ) ;
if ( opts & CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS ) {
if ( peinfo - > nsections = = 1 ) {
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
for ( j = i ; j < ( size_t ) ( peinfo - > nsections - 1 ) ; j + + )
memcpy ( & ( peinfo - > sections [ j ] ) , & ( peinfo - > sections [ j + 1 ] ) , sizeof ( struct cli_exe_section ) ) ;
for ( j = i ; j < ( size_t ) ( peinfo - > nsections - 1 ) ; j + + )
memcpy ( & section_hdrs [ j ] , & section_hdrs [ j + 1 ] , sizeof ( struct pe_image_section_hdr ) ) ;
peinfo - > nsections - - ;
// Adjust i since we removed a section and continue on
i - - ;
continue ;
} else {
section - > rsz = 0 ;
section - > ursz = 0 ;
}
} else {
/* If a section is truncated, adjust it's size value */
if ( ! CLI_ISCONTAINED_0_TO ( fsize , section - > raw , section - > rsz ) ) {
cli_dbgmsg ( " cli_peheader: PE Section %zu raw+rsz extends past the end of the file by %lu bytes \n " , section_pe_idx , ( section - > raw + section - > rsz ) - fsize ) ;
section - > rsz = fsize - section - > raw ;
}
if ( ! CLI_ISCONTAINED_0_TO ( fsize , section - > uraw , section - > ursz ) ) {
cli_dbgmsg ( " cli_peheader: PE Section %zu uraw+ursz extends past the end of the file by %lu bytes \n " , section_pe_idx , ( section - > uraw + section - > ursz ) - fsize ) ;
section - > ursz = fsize - section - > uraw ;
}
}
}
strncpy ( sname , ( char * ) section_hdr - > Name , 8 ) ;
sname [ 8 ] = ' \0 ' ;
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
add_section_info ( ctx , & peinfo - > sections [ i ] ) ;
if ( cli_json_timeout_cycle_check ( ctx , & toval ) ! = CL_SUCCESS ) {
2023-01-14 18:28:39 +08:00
ret = CL_ETIMEOUT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
}
# endif
// TODO Why do we do this
// TODO Should this be done before we dump the json
if ( ! section - > vsz & & section - > rsz )
section - > vsz = PESALIGN ( section - > ursz , salign ) ;
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) {
cli_dbgmsg ( " Section %zu \n " , section_pe_idx ) ;
cli_dbgmsg ( " Section name: %s \n " , sname ) ;
cli_dbgmsg ( " Section data (from headers - in memory) \n " ) ;
cli_dbgmsg ( " VirtualSize: 0x%x 0x%x \n " , section - > uvsz , section - > vsz ) ;
cli_dbgmsg ( " VirtualAddress: 0x%x 0x%x \n " , section - > urva , section - > rva ) ;
cli_dbgmsg ( " SizeOfRawData: 0x%x 0x%x \n " , section - > ursz , section - > rsz ) ;
cli_dbgmsg ( " PointerToRawData: 0x%x 0x%x \n " , section - > uraw , section - > raw ) ;
if ( section - > chr & 0x20 ) {
cli_dbgmsg ( " Section contains executable code \n " ) ;
}
if ( section - > vsz < section - > rsz ) {
cli_dbgmsg ( " Section contains free space \n " ) ;
/*
cli_dbgmsg ( " Dumping %d bytes \n " , section_hdr . SizeOfRawData - section_hdr . VirtualSize ) ;
ddump ( desc , section_hdr . PointerToRawData + section_hdr . VirtualSize , section_hdr . SizeOfRawData - section_hdr . VirtualSize , cli_gentemp ( NULL ) ) ;
*/
}
if ( section - > chr & 0x20000000 )
cli_dbgmsg ( " Section's memory is executable \n " ) ;
if ( section - > chr & 0x80000000 )
cli_dbgmsg ( " Section's memory is writeable \n " ) ;
cli_dbgmsg ( " ------------------------------------ \n " ) ;
}
if ( ! salign | | ( section - > urva % salign ) ) { /* Bad section alignment */
cli_dbgmsg ( " cli_peheader: Broken PE - section's VirtualAddress is misaligned \n " ) ;
if ( opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS ) {
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
}
// TODO should we skip all of these checks if it's an empty
// section? Why the exception for uraw?
if ( section - > urva > > 31 | | section - > uvsz > > 31 | | ( section - > rsz & & section - > uraw > > 31 ) | | peinfo - > sections [ i ] . ursz > > 31 ) {
cli_dbgmsg ( " cli_peheader: Found PE values with sign bit set \n " ) ;
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
if ( ! i ) {
if ( section - > urva ! = peinfo - > hdr_size ) { /* Bad first section RVA */
cli_dbgmsg ( " cli_peheader: First section doesn't start immediately after the header \n " ) ;
if ( opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS ) {
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
}
peinfo - > min = section - > rva ;
peinfo - > max = section - > rva + section - > rsz ;
} else {
if ( section - > urva - peinfo - > sections [ i - 1 ] . urva ! = peinfo - > sections [ i - 1 ] . vsz ) { /* No holes, no overlapping, no virtual disorder */
cli_dbgmsg ( " cli_peheader: Virtually misplaced section (wrong order, overlapping, non contiguous) \n " ) ;
if ( opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS ) {
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
}
if ( section - > rva < peinfo - > min )
peinfo - > min = section - > rva ;
if ( section - > rva + section - > rsz > peinfo - > max ) {
peinfo - > max = section - > rva + section - > rsz ;
peinfo - > overlay_start = section - > raw + section - > rsz ;
}
// TODO This case might be possible, which would lead to us
// mislabelling the overlay
if ( section - > raw + section - > rsz > peinfo - > max ) {
cli_dbgmsg ( " cli_peheader: Assumption Violated: Last section end RVA isn't tied to the last section \n " ) ;
}
}
}
peinfo - > overlay_size = fsize - peinfo - > overlay_start ;
// NOTE: For DLLs the entrypoint is likely to be zero
// TODO Should this offset include peinfo->offset?
if ( ! ( peinfo - > ep = cli_rawaddr ( peinfo - > vep , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ) & & err ) {
cli_dbgmsg ( " cli_peheader: Broken PE file - Can't map EntryPoint to a file offset \n " ) ;
2023-01-14 18:28:39 +08:00
ret = CL_EFORMAT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
# if HAVE_JSON
if ( opts & CLI_PEHEADER_OPT_COLLECT_JSON ) {
cli_jsonint ( pe_json , " EntryPointOffset " , peinfo - > ep ) ;
if ( cli_json_timeout_cycle_check ( ctx , & toval ) ! = CL_SUCCESS ) {
2023-01-14 18:28:39 +08:00
ret = CL_ETIMEOUT ;
2022-10-22 18:41:00 +08:00
goto done ;
}
}
# endif
if ( opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ) {
cli_dbgmsg ( " EntryPoint offset: 0x%x (%d) \n " , peinfo - > ep , peinfo - > ep ) ;
}
if ( is_dll | | peinfo - > ndatadirs < 3 | | ! peinfo - > dirs [ 2 ] . Size )
peinfo - > res_addr = 0 ;
else
peinfo - > res_addr = EC32 ( peinfo - > dirs [ 2 ] . VirtualAddress ) ;
while ( opts & CLI_PEHEADER_OPT_EXTRACT_VINFO & &
peinfo - > ndatadirs > = 3 & & peinfo - > dirs [ 2 ] . Size ) {
struct vinfo_list vlist ;
const uint8_t * vptr , * baseptr ;
uint32_t rva , res_sz ;
// TODO This code assumes peinfo->offset == 0, which might not always
// be the case.
if ( 0 ! = peinfo - > offset ) {
cli_dbgmsg ( " cli_peheader: Assumption Violated: Looking for version info when peinfo->offset != 0 \n " ) ;
}
memset ( & vlist , 0 , sizeof ( vlist ) ) ;
findres ( 0x10 , 0xffffffff , map , peinfo , versioninfo_cb , & vlist ) ;
if ( ! vlist . count )
break ; /* No version_information */
if ( cli_hashset_init ( & peinfo - > vinfo , 32 , 80 ) ) {
cli_errmsg ( " cli_peheader: Unable to init vinfo hashset \n " ) ;
goto done ;
}
err = 0 ;
for ( i = 0 ; i < vlist . count ; i + + ) { /* enum all version_information res - RESUMABLE */
cli_dbgmsg ( " cli_peheader: parsing version info @ rva %x (%zu/%u) \n " , vlist . rvas [ i ] , i + 1 , vlist . count ) ;
rva = cli_rawaddr ( vlist . rvas [ i ] , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( err )
continue ;
if ( ! ( vptr = fmap_need_off_once ( map , rva , 16 ) ) )
continue ;
baseptr = vptr - rva ;
/* parse resource */
rva = cli_readint32 ( vptr ) ; /* ptr to version_info */
res_sz = cli_readint32 ( vptr + 4 ) ; /* sizeof(resource) */
rva = cli_rawaddr ( rva , peinfo - > sections , peinfo - > nsections , & err , fsize , peinfo - > hdr_size ) ;
if ( err )
continue ;
if ( ! ( vptr = fmap_need_off_once ( map , rva , res_sz ) ) )
continue ;
while ( res_sz > 4 ) { /* look for version_info - NOT RESUMABLE (expecting exactly one versioninfo) */
uint32_t vinfo_sz , vinfo_val_sz , got_varfileinfo = 0 ;
vinfo_sz = vinfo_val_sz = cli_readint32 ( vptr ) ;
vinfo_sz & = 0xffff ;
if ( vinfo_sz > res_sz )
break ; /* the content is larger than the container */
vinfo_val_sz > > = 16 ;
if ( vinfo_sz < = 6 + 0x20 + 2 + 0x34 | |
vinfo_val_sz ! = 0x34 | |
memcmp ( vptr + 6 , " V \0 S \0 _ \0 V \0 E \0 R \0 S \0 I \0 O \0 N \0 _ \0 I \0 N \0 F \0 O \0 \0 \0 " , 0x20 ) | |
( unsigned int ) cli_readint32 ( vptr + 0x28 ) ! = 0xfeef04bd ) {
/* - there should be enough room for the header(6), the key "VS_VERSION_INFO"(20), the padding(2) and the value(34)
* - the value should be sizeof ( fixedfileinfo )
* - the key should match
* - there should be some proper magic for fixedfileinfo */
break ; /* there's no point in looking further */
}
/* move to the end of fixedfileinfo where the child elements are located */
vptr + = 6 + 0x20 + 2 + 0x34 ;
vinfo_sz - = 6 + 0x20 + 2 + 0x34 ;
while ( vinfo_sz > 6 ) { /* look for stringfileinfo - NOT RESUMABLE (expecting at most one stringfileinfo) */
uint32_t sfi_sz = cli_readint32 ( vptr ) & 0xffff ;
if ( sfi_sz > vinfo_sz )
break ; /* the content is larger than the container */
if ( ! got_varfileinfo & & sfi_sz > 6 + 0x18 & & ! memcmp ( vptr + 6 , " V \0 a \0 r \0 F \0 i \0 l \0 e \0 I \0 n \0 f \0 o \0 \0 \0 " , 0x18 ) ) {
/* skip varfileinfo as it sometimes appear before stringtableinfo */
vptr + = sfi_sz ;
vinfo_sz - = sfi_sz ;
got_varfileinfo = 1 ;
continue ;
}
if ( sfi_sz < = 6 + 0x1e | | memcmp ( vptr + 6 , " S \0 t \0 r \0 i \0 n \0 g \0 F \0 i \0 l \0 e \0 I \0 n \0 f \0 o \0 \0 \0 " , 0x1e ) ) {
/* - there should be enough room for the header(6) and the key "StringFileInfo"(1e)
* - the key should match */
break ; /* this is an implicit hard fail: parent is not resumable */
}
/* move to the end of stringfileinfo where the child elements are located */
vptr + = 6 + 0x1e ;
sfi_sz - = 6 + 0x1e ;
while ( sfi_sz > 6 ) { /* enum all stringtables - RESUMABLE */
uint32_t st_sz = cli_readint32 ( vptr ) & 0xffff ;
const uint8_t * next_vptr = vptr + st_sz ;
uint32_t next_sfi_sz = sfi_sz - st_sz ;
if ( st_sz > sfi_sz | | st_sz < = 24 ) {
/* - the content is larger than the container
- there ' s no room for a stringtables ( headers ( 6 ) + key ( 16 ) + padding ( 2 ) ) */
break ; /* this is an implicit hard fail: parent is not resumable */
}
/* move to the end of stringtable where the child elements are located */
vptr + = 24 ;
st_sz - = 24 ;
while ( st_sz > 6 ) { /* enum all strings - RESUMABLE */
uint32_t s_sz , s_key_sz , s_val_sz ;
s_sz = ( cli_readint32 ( vptr ) & 0xffff ) + 3 ;
s_sz & = ~ 3 ;
if ( s_sz > st_sz | | s_sz < = 6 + 2 + 8 ) {
/* - the content is larger than the container
* - there ' s no room for a minimal string
* - there ' s no room for the value */
st_sz = 0 ;
sfi_sz = 0 ;
break ; /* force a hard fail */
}
/* ~wcstrlen(key) */
for ( s_key_sz = 6 ; s_key_sz + 1 < s_sz ; s_key_sz + = 2 ) {
if ( vptr [ s_key_sz ] | | vptr [ s_key_sz + 1 ] )
continue ;
s_key_sz + = 2 ;
break ;
}
s_key_sz + = 3 ;
s_key_sz & = ~ 3 ;
if ( s_key_sz > = s_sz ) {
/* key overflow */
vptr + = s_sz ;
st_sz - = s_sz ;
continue ;
}
s_val_sz = s_sz - s_key_sz ;
s_key_sz - = 6 ;
if ( s_val_sz < = 2 ) {
/* skip unset value */
vptr + = s_sz ;
st_sz - = s_sz ;
continue ;
}
if ( cli_hashset_addkey ( & peinfo - > vinfo , ( uint32_t ) ( vptr - baseptr + 6 ) ) ) {
cli_errmsg ( " cli_peheader: Unable to add rva to vinfo hashset \n " ) ;
goto done ;
}
if ( cli_debug_flag ) {
char * k , * v , * s ;
/* FIXME: skip too long strings */
k = cli_utf16toascii ( ( const char * ) vptr + 6 , s_key_sz ) ;
if ( k ) {
v = cli_utf16toascii ( ( const char * ) vptr + s_key_sz + 6 , s_val_sz ) ;
if ( v ) {
s = cli_str2hex ( ( const char * ) vptr + 6 , s_key_sz + s_val_sz ) ;
if ( s ) {
cli_dbgmsg ( " VersionInfo (%x): '%s'='%s' - VI:%s \n " , ( uint32_t ) ( vptr - baseptr + 6 ) , k , v , s ) ;
free ( s ) ;
}
free ( v ) ;
}
free ( k ) ;
}
}
vptr + = s_sz ;
st_sz - = s_sz ;
} /* enum all strings - RESUMABLE */
vptr = next_vptr ;
sfi_sz = next_sfi_sz * ( sfi_sz ! = 0 ) ;
} /* enum all stringtables - RESUMABLE */
break ;
} /* look for stringfileinfo - NOT RESUMABLE */
break ;
} /* look for version_info - NOT RESUMABLE */
} /* enum all version_information res - RESUMABLE */
break ;
} /* while(dirs[2].Size) */
// Do final preperations for peinfo to be passed back
peinfo - > is_dll = is_dll ;
2023-01-14 18:28:39 +08:00
ret = CL_SUCCESS ;
2022-10-22 18:41:00 +08:00
done :
/* In the fail case, peinfo will get destroyed by the caller */
if ( NULL ! = section_hdrs ) {
free ( section_hdrs ) ;
}
return ret ;
}
// TODO We should sort based on VirtualAddress instead, since PointerToRawData
// will be zero for sections where SizeOfRawData is zero. This also aligns
// with what tools like pefile do.
static int sort_sects ( const void * first , const void * second )
{
const struct cli_exe_section * a = first , * b = second ;
return ( a - > raw - b - > raw ) ;
}
2023-01-14 18:28:39 +08:00
/* Check the given PE file for an authenticode signature and return whether
2022-10-22 18:41:00 +08:00
* the signature is valid . There are two cases that this function should
* handle :
* - A PE file has an embedded Authenticode section
* - The PE file has no embedded Authenticode section but is covered by a
* catalog file that was loaded in via a - d
*
* If peinfo is NULL , one will be created internally and used
*
2023-01-14 18:28:39 +08:00
* CL_VERIFIED will be returned if the file was trusted based on its
* signature . CL_VIRUS will be returned if the file was blocked based on
2022-10-22 18:41:00 +08:00
* its signature . Otherwise , a cl_error_t error value will be returned .
*
* If CL_VIRUS is returned , cli_append_virus will get called , adding the
2023-01-14 18:28:39 +08:00
* name associated with the block list CRB rules to the list of found viruses . */
2022-10-22 18:41:00 +08:00
cl_error_t cli_check_auth_header ( cli_ctx * ctx , struct cli_exe_info * peinfo )
{
size_t at ;
2023-01-14 18:28:39 +08:00
unsigned int i , j , hlen ;
2022-10-22 18:41:00 +08:00
size_t fsize ;
fmap_t * map = ctx - > fmap ;
void * hashctx = NULL ;
struct pe_certificate_hdr cert_hdr ;
struct cli_mapped_region * regions = NULL ;
unsigned int nregions ;
cl_error_t ret = CL_EVERIFY ;
2023-01-14 18:28:39 +08:00
uint8_t authsha [ SHA256_HASH_SIZE ] ;
2022-10-22 18:41:00 +08:00
uint32_t sec_dir_offset ;
uint32_t sec_dir_size ;
struct cli_exe_info _peinfo ;
// If Authenticode parsing has been disabled via DCONF or an engine
// option, then don't continue on.
if ( ! ( DCONF & PE_CONF_CERTS ) )
return CL_EVERIFY ;
if ( ctx - > engine - > engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS )
return CL_EVERIFY ;
// If peinfo is NULL, initialize one. This makes it so that this function
// can be used easily by sigtool
if ( NULL = = peinfo ) {
peinfo = & _peinfo ;
cli_exe_info_init ( peinfo , 0 ) ;
2023-01-14 18:28:39 +08:00
if ( CL_SUCCESS ! = cli_peheader ( ctx - > fmap , peinfo , CLI_PEHEADER_OPT_NONE , NULL ) ) {
2022-10-22 18:41:00 +08:00
cli_exe_info_destroy ( peinfo ) ;
return CL_EFORMAT ;
}
}
sec_dir_offset = EC32 ( peinfo - > dirs [ 4 ] . VirtualAddress ) ;
sec_dir_size = EC32 ( peinfo - > dirs [ 4 ] . Size ) ;
// As an optimization, check the security DataDirectory here and if
// it's less than 8-bytes (and we aren't relying on this code to compute
// the section hashes), bail out if we don't have any Authenticode hashes
2023-01-14 18:28:39 +08:00
// loaded from .cat files. The value 2 in these calls is the sentinel value
// for the 'PE' .cat Authenticode hash file type.
if ( sec_dir_size < 8 & &
! cli_hm_have_size ( ctx - > engine - > hm_fp , CLI_HASH_SHA1 , 2 ) & &
! cli_hm_have_size ( ctx - > engine - > hm_fp , CLI_HASH_SHA256 , 2 ) ) {
2022-10-22 18:41:00 +08:00
ret = CL_BREAK ;
goto finish ;
}
fsize = map - > len ;
// We'll build a list of the regions that need to be hashed and pass it to
// asn1_check_mscat to do hash verification there (the hash algorithm is
// specified in the PKCS7 structure). We need to hash up to 4 regions
regions = ( struct cli_mapped_region * ) cli_calloc ( 4 , sizeof ( struct cli_mapped_region ) ) ;
if ( ! regions ) {
ret = CL_EMEM ;
goto finish ;
}
nregions = 0 ;
# define add_chunk_to_hash_list(_offset, _size) \
do { \
regions [ nregions ] . offset = ( _offset ) ; \
regions [ nregions ] . size = ( _size ) ; \
nregions + + ; \
} while ( 0 )
// Pretty much every case below should return CL_EFORMAT
ret = CL_EFORMAT ;
/* MZ to checksum */
at = 0 ;
hlen = peinfo - > e_lfanew + sizeof ( struct pe_image_file_hdr ) + ( peinfo - > is_pe32plus ? offsetof ( struct pe_image_optional_hdr64 , CheckSum ) : offsetof ( struct pe_image_optional_hdr32 , CheckSum ) ) ;
add_chunk_to_hash_list ( 0 , hlen ) ;
at = hlen + 4 ;
/* Checksum to security */
if ( peinfo - > is_pe32plus )
hlen = sizeof ( struct pe_image_optional_hdr64 ) - offsetof ( struct pe_image_optional_hdr64 , CheckSum ) - 4 ;
else
hlen = sizeof ( struct pe_image_optional_hdr32 ) - offsetof ( struct pe_image_optional_hdr32 , CheckSum ) - 4 ;
hlen + = sizeof ( struct pe_image_data_dir ) * 4 ;
add_chunk_to_hash_list ( at , hlen ) ;
at + = hlen + 8 ;
if ( at > peinfo - > hdr_size ) {
goto finish ;
}
if ( sec_dir_offset ) {
// Verify that we have all the bytes we expect in the authenticode sig
// and that the certificate table is the last thing in the file
// (according to the MS13-098 bulletin, this is a requirement)
if ( fsize ! = sec_dir_size + sec_dir_offset ) {
cli_dbgmsg ( " cli_check_auth_header: expected authenticode data at the end of the file \n " ) ;
goto finish ;
}
// Hash everything else up to the start of the security section. Allow
// the case where at == sec_dir_offset without adding another region
// to hash, since this could technically be valid (although I haven't
// verified this).
if ( at < sec_dir_offset ) {
hlen = sec_dir_offset - at ;
add_chunk_to_hash_list ( at , hlen ) ;
} else if ( at > sec_dir_offset ) {
cli_dbgmsg ( " cli_check_auth_header: security directory offset appears to overlap with the PE header \n " ) ;
goto finish ;
}
// Parse the security directory header
if ( fmap_readn ( map , & cert_hdr , sec_dir_offset , sizeof ( cert_hdr ) ) ! = sizeof ( cert_hdr ) ) {
goto finish ;
}
if ( EC16 ( cert_hdr . revision ) ! = WIN_CERT_REV_2 ) {
cli_dbgmsg ( " cli_check_auth_header: unsupported authenticode data revision \n " ) ;
goto finish ;
}
if ( EC16 ( cert_hdr . type ) ! = WIN_CERT_TYPE_PKCS7 ) {
cli_dbgmsg ( " cli_check_auth_header: unsupported authenticode data type \n " ) ;
goto finish ;
}
hlen = sec_dir_size ;
if ( EC32 ( cert_hdr . length ) ! = hlen ) {
/* This is the case that MS13-098 aimed to address, but it got
2023-01-14 18:28:39 +08:00
* pushback to where the fix ( not allowing additional , non - zero
* bytes in the security directory ) is now opt - in via a registry
* key . Given that most machines will treat these binaries as
* valid , we ' ll still parse the signature and just trust that
* our trust signatures are tailored enough to where any
* instances of this are reasonable ( for instance , I saw one
* binary that appeared to use this to embed a license key . ) */
2022-10-22 18:41:00 +08:00
cli_dbgmsg ( " cli_check_auth_header: MS13-098 violation detected, but continuing on to verify certificate \n " ) ;
}
at = sec_dir_offset + sizeof ( cert_hdr ) ;
hlen - = sizeof ( cert_hdr ) ;
ret = asn1_check_mscat ( ( struct cl_engine * ) ( ctx - > engine ) , map , at , hlen , regions , nregions , ctx ) ;
if ( CL_VERIFIED = = ret ) {
// We validated the embedded signature. Hooray!
goto finish ;
} else if ( CL_VIRUS = = ret ) {
2023-01-14 18:28:39 +08:00
// A block list rule hit - don't continue on to check hm_fp for a match
2022-10-22 18:41:00 +08:00
goto finish ;
}
// Otherwise, we still need to check to see whether this file is
// covered by a .cat file (it's common these days for driver files
// to have .cat files covering PEs with embedded signatures)
} else {
// Hash everything else
if ( at < fsize ) {
hlen = fsize - at ;
add_chunk_to_hash_list ( at , hlen ) ;
}
}
// At this point we should compute the SHA1 authenticode hash to see
// whether we've had any hashes added from external catalog files
2023-01-14 18:28:39 +08:00
static const struct supported_hashes {
const cli_hash_type_t hashtype ;
const char * hashctx_name ;
} supported_hashes [ ] = {
{ CLI_HASH_SHA1 , " sha1 " } ,
{ CLI_HASH_SHA256 , " sha256 " } ,
} ;
for ( i = 0 ; i < ( sizeof ( supported_hashes ) / sizeof ( supported_hashes [ 0 ] ) ) ; i + + ) {
const cli_hash_type_t hashtype = supported_hashes [ i ] . hashtype ;
const char * hashctx_name = supported_hashes [ i ] . hashctx_name ;
if ( ! cli_hm_have_size ( ctx - > engine - > hm_fp , hashtype , 2 ) ) {
2022-10-22 18:41:00 +08:00
continue ;
}
2023-01-14 18:28:39 +08:00
hashctx = cl_hash_init ( hashctx_name ) ;
if ( NULL = = hashctx ) {
ret = CL_EMEM ;
goto finish ;
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
for ( j = 0 ; j < nregions ; j + + ) {
const uint8_t * hptr ;
if ( 0 = = regions [ j ] . size ) {
continue ;
}
if ( ! ( hptr = fmap_need_off_once ( map , regions [ j ] . offset , regions [ j ] . size ) ) ) {
break ;
}
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
cl_update_hash ( hashctx , hptr , regions [ j ] . size ) ;
}
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
if ( j ! = nregions ) {
goto finish ;
}
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
cl_finish_hash ( hashctx , authsha ) ;
hashctx = NULL ;
if ( cli_hm_scan ( authsha , 2 , NULL , ctx - > engine - > hm_fp , hashtype ) = = CL_VIRUS ) {
cli_dbgmsg ( " cli_check_auth_header: PE file trusted by catalog file (%s) \n " , hashctx_name ) ;
ret = CL_VERIFIED ;
goto finish ;
}
2022-10-22 18:41:00 +08:00
}
ret = CL_EVERIFY ;
finish :
if ( NULL ! = hashctx ) {
cl_hash_destroy ( hashctx ) ;
}
if ( NULL ! = regions ) {
free ( regions ) ;
}
// If we created the peinfo, then destroy it. Otherwise we don't own it
if ( & _peinfo = = peinfo ) {
cli_exe_info_destroy ( peinfo ) ;
}
return ret ;
}
/* Print out either the MD5, SHA1, or SHA256 associated with the imphash or
* the individual sections . Also , this function computes the hashes of each
* section ( sorted based on the RVAs of the sections ) if hashes is non - NULL .
*
* If the section hashes are to be computed and returned , this function
* allocates memory for the section hashes , and it ' s up to the caller to free
* it . hashes - > sections will be initialized to NULL at the beginning of the
* function , and if after the call it ' s value is non - NULL , the memory should be
* freed . Furthermore , if hashes - > sections is non - NULL , the hashes can assume
* to be valid regardless of the return code .
*
* Also , a few other notes :
* - If a section has a virtual size of zero , it ' s corresponding hash value
* will not be computed and the hash contents will be all zeroes .
* - If a section extends beyond the end of the file , the section data and
* length will be truncated , and the hash generated accordingly
* - If a section exists completely outside of the file , it won ' t be included
* in the list of sections , and nsections will be adjusted accordingly .
*/
cl_error_t cli_genhash_pe ( cli_ctx * ctx , unsigned int class , int type , stats_section_t * hashes )
{
unsigned int i ;
struct cli_exe_info _peinfo ;
struct cli_exe_info * peinfo = & _peinfo ;
unsigned char * hash , * hashset [ CLI_HASH_AVAIL_TYPES ] ;
int genhash [ CLI_HASH_AVAIL_TYPES ] ;
int hlen = 0 ;
if ( hashes ) {
hashes - > sections = NULL ;
if ( class ! = CL_GENHASH_PE_CLASS_SECTION | | type ! = 1 ) {
cli_dbgmsg ( " `hashes` can only be populated with MD5 PE section data \n " ) ;
return CL_EARG ;
}
}
if ( class > = CL_GENHASH_PE_CLASS_LAST )
return CL_EARG ;
// TODO see if peinfo can be passed in (or lives in ctx or something) and
// if so, use that to avoid having to re-parse the header
cli_exe_info_init ( peinfo , 0 ) ;
2023-01-14 18:28:39 +08:00
if ( cli_peheader ( ctx - > fmap , peinfo , CLI_PEHEADER_OPT_NONE , NULL ) ! = CL_SUCCESS ) {
2022-10-22 18:41:00 +08:00
cli_exe_info_destroy ( peinfo ) ;
return CL_EFORMAT ;
}
cli_qsort ( peinfo - > sections , peinfo - > nsections , sizeof ( * ( peinfo - > sections ) ) , sort_sects ) ;
/* pick hashtypes to generate */
memset ( genhash , 0 , sizeof ( genhash ) ) ;
memset ( hashset , 0 , sizeof ( hashset ) ) ;
switch ( type ) {
case 1 :
genhash [ CLI_HASH_MD5 ] = 1 ;
hlen = hashlen [ CLI_HASH_MD5 ] ;
hash = hashset [ CLI_HASH_MD5 ] = cli_calloc ( hlen , sizeof ( char ) ) ;
break ;
case 2 :
genhash [ CLI_HASH_SHA1 ] = 1 ;
hlen = hashlen [ CLI_HASH_SHA1 ] ;
hash = hashset [ CLI_HASH_SHA1 ] = cli_calloc ( hlen , sizeof ( char ) ) ;
break ;
default :
genhash [ CLI_HASH_SHA256 ] = 1 ;
hlen = hashlen [ CLI_HASH_SHA256 ] ;
hash = hashset [ CLI_HASH_SHA256 ] = cli_calloc ( hlen , sizeof ( char ) ) ;
break ;
}
if ( ! hash ) {
cli_errmsg ( " cli_genhash_pe: cli_malloc failed! \n " ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_EMEM ;
}
if ( hashes ) {
hashes - > nsections = peinfo - > nsections ;
hashes - > sections = cli_calloc ( peinfo - > nsections , sizeof ( struct cli_section_hash ) ) ;
if ( ! ( hashes - > sections ) ) {
cli_exe_info_destroy ( peinfo ) ;
free ( hash ) ;
return CL_EMEM ;
}
}
if ( class = = CL_GENHASH_PE_CLASS_SECTION ) {
char * dstr ;
for ( i = 0 ; i < peinfo - > nsections ; i + + ) {
/* Generate hashes */
if ( cli_hashsect ( ctx - > fmap , & peinfo - > sections [ i ] , hashset , genhash , genhash ) = = 1 ) {
if ( cli_debug_flag ) {
dstr = cli_str2hex ( ( char * ) hash , hlen ) ;
cli_dbgmsg ( " Section{%u}: %u:%s \n " , i , peinfo - > sections [ i ] . rsz , dstr ? ( char * ) dstr : " (NULL) " ) ;
if ( dstr ! = NULL ) {
free ( dstr ) ;
}
}
if ( hashes ) {
memcpy ( hashes - > sections [ i ] . md5 , hash , sizeof ( hashes - > sections [ i ] . md5 ) ) ;
hashes - > sections [ i ] . len = peinfo - > sections [ i ] . rsz ;
}
} else if ( peinfo - > sections [ i ] . rsz ) {
cli_dbgmsg ( " Section{%u}: failed to generate hash for section \n " , i ) ;
} else {
cli_dbgmsg ( " Section{%u}: section contains no data \n " , i ) ;
}
}
} else if ( class = = CL_GENHASH_PE_CLASS_IMPTBL ) {
char * dstr ;
uint32_t impsz = 0 ;
cl_error_t ret ;
/* Generate hash */
ret = hash_imptbl ( ctx , hashset , & impsz , genhash , peinfo ) ;
if ( ret = = CL_SUCCESS ) {
if ( cli_debug_flag ) {
dstr = cli_str2hex ( ( char * ) hash , hlen ) ;
cli_dbgmsg ( " Imphash: %s:%u \n " , dstr ? ( char * ) dstr : " (NULL) " , impsz ) ;
if ( dstr ! = NULL ) {
free ( dstr ) ;
}
}
} else {
cli_dbgmsg ( " Imphash: failed to generate hash for import table (%d) \n " , ret ) ;
}
} else {
cli_dbgmsg ( " cli_genhash_pe: unknown pe genhash class: %u \n " , class ) ;
}
free ( hash ) ;
cli_exe_info_destroy ( peinfo ) ;
return CL_SUCCESS ;
}