这近2000行的代码,要是没有Source Insight,都不知道怎么看下去。跟着跟着来到了PE文件查杀的地方,发现前面都中规中矩地进行PE属性检查,中间一段开始扫描每个区块,然后和特征库的size对比扫描查毒。再后面,加了对一些流行病毒的特定查杀。(这个比较晕。)
代码注释如下(代码过长,可能有些人加载较慢,请耐心等待……):
//传说中的PE文件查杀
int cli_scanpe(int desc, cli_ctx *ctx)
{
uint16_t e_magic; /* DOS signature ("MZ") */
uint16_t nsections;
uint32_t e_lfanew; /* address of new exe header */
uint32_t ep, vep; /* entry point (raw, virtual) */
uint8_t polipos = 0;
time_t timestamp;
struct pe_image_file_hdr file_hdr;
union {
struct pe_image_optional_hdr64 opt64;
struct pe_image_optional_hdr32 opt32;
} pe_opt;
struct pe_image_section_hdr *section_hdr;
struct stat sb;
char sname[9], buff[4096], epbuff[4096], *tempfile;
uint32_t epsize;
ssize_t bytes;
unsigned int i, found, upx_success = 0, min = 0, max = 0, err;
unsigned int ssize = 0, dsize = 0, dll = 0, pe_plus = 0;
int (*upxfn)(char *, uint32_t, char *, uint32_t *, uint32_t, uint32_t, uint32_t) = NULL;
char *src = NULL, *dest = NULL;
int ndesc, ret = CL_CLEAN, upack = 0, native=0;
size_t fsize;
uint32_t valign, falign, hdr_size, j;
struct cli_exe_section *exe_sections;
struct cli_matcher *md5_sect;
char timestr[32];
if(!ctx) {
cli_errmsg("cli_scanpe: ctx == NULL\n");
return CL_ENULLARG;
}
//读取DOS头,既是MZ
if(cli_readn(desc, &e_magic, sizeof(e_magic)) != sizeof(e_magic)) {
cli_dbgmsg("Can't read DOS signature\n");
return CL_CLEAN;
}
//EC16功能:前后16位数据互换
//因为IMAGE_DOS_SIGNATURE是0x5a4d,既是ZM
if(EC16(e_magic) != IMAGE_DOS_SIGNATURE && EC16(e_magic) != IMAGE_DOS_SIGNATURE_OLD) {
cli_dbgmsg("Invalid DOS signature\n");
return CL_CLEAN;
}
//再跳过58bytes就到达0x3c位置,PE头偏移数据
lseek(desc, 58, SEEK_CUR); /* skip to the end of the DOS header */
//读取PE文件头的文件偏移地址
if(cli_readn(desc, &e_lfanew, sizeof(e_lfanew)) != sizeof(e_lfanew)) {
cli_dbgmsg("Can't read new header address\n");
/* truncated header? */
//根据设置,判断是否要归类到病毒
if(DETECT_BROKEN) {
if(ctx->virname)
*ctx->virname = "Broken.Executable";
return CL_VIRUS;
}
return CL_CLEAN;
}
//EC32功能:高低地址翻转
//设置为正常顺序
e_lfanew = EC32(e_lfanew);
cli_dbgmsg("e_lfanew == %d\n", e_lfanew);
if(!e_lfanew) {
cli_dbgmsg("Not a PE file\n");
return CL_CLEAN;
}
//跳到PE文件头位置
if(lseek(desc, e_lfanew, SEEK_SET) < 0) {
/* probably not a PE file */
cli_dbgmsg("Can't lseek to e_lfanew\n");
return CL_CLEAN;
}
//读取PE镜像文件头数据
if(cli_readn(desc, &file_hdr, sizeof(struct pe_image_file_hdr)) != sizeof(struct pe_image_file_hdr)) {
/* bad information in e_lfanew - probably not a PE file */
cli_dbgmsg("Can't read file header\n");
return CL_CLEAN;
}
//判断PE签名是否为00EP
if(EC32(file_hdr.Magic) != IMAGE_NT_SIGNATURE) {
cli_dbgmsg("Invalid PE signature (probably NE file)\n");
return CL_CLEAN;
}
//文件属性为0x2000既是DLL文件
//文件属性为0x01则是普通EXE文件
if(EC16(file_hdr.Characteristics) & 0x2000) {
cli_dbgmsg("File type: DLL\n");
dll = 1;
} else if(EC16(file_hdr.Characteristics) & 0x01) {
cli_dbgmsg("File type: Executable\n");
}
//判断该PE文件的目标CPU 类型
switch(EC16(file_hdr.Machine)) {
case 0x0:
cli_dbgmsg("Machine type: Unknown\n");
break;
case 0x14c:
cli_dbgmsg("Machine type: 80386\n");
break;
case 0x14d:
cli_dbgmsg("Machine type: 80486\n");
break;
case 0x14e:
cli_dbgmsg("Machine type: 80586\n");
break;
case 0x160:
cli_dbgmsg("Machine type: R30000 (big-endian)\n");
break;
case 0x162:
cli_dbgmsg("Machine type: R3000\n");
break;
case 0x166:
cli_dbgmsg("Machine type: R4000\n");
break;
case 0x168:
cli_dbgmsg("Machine type: R10000\n");
break;
case 0x184:
cli_dbgmsg("Machine type: DEC Alpha AXP\n");
break;
case 0x284:
cli_dbgmsg("Machine type: DEC Alpha AXP 64bit\n");
break;
case 0x1f0:
cli_dbgmsg("Machine type: PowerPC\n");
break;
case 0x200:
cli_dbgmsg("Machine type: IA64\n");
break;
case 0x268:
cli_dbgmsg("Machine type: M68k\n");
break;
case 0x266:
cli_dbgmsg("Machine type: MIPS16\n");
break;
case 0x366:
cli_dbgmsg("Machine type: MIPS+FPU\n");
break;
case 0x466:
cli_dbgmsg("Machine type: MIPS16+FPU\n");
break;
case 0x1a2:
cli_dbgmsg("Machine type: Hitachi SH3\n");
break;
case 0x1a3:
cli_dbgmsg("Machine type: Hitachi SH3-DSP\n");
break;
case 0x1a4:
cli_dbgmsg("Machine type: Hitachi SH3-E\n");
break;
case 0x1a6:
cli_dbgmsg("Machine type: Hitachi SH4\n");
break;
case 0x1a8:
cli_dbgmsg("Machine type: Hitachi SH5\n");
break;
case 0x1c0:
cli_dbgmsg("Machine type: ARM\n");
break;
case 0x1c2:
cli_dbgmsg("Machine type: THUMB\n");
break;
case 0x1d3:
cli_dbgmsg("Machine type: AM33\n");
break;
case 0x520:
cli_dbgmsg("Machine type: Infineon TriCore\n");
break;
case 0xcef:
cli_dbgmsg("Machine type: CEF\n");
break;
case 0xebc:
cli_dbgmsg("Machine type: EFI Byte Code\n");
break;
case 0x9041:
cli_dbgmsg("Machine type: M32R\n");
break;
case 0xc0ee:
cli_dbgmsg("Machine type: CEE\n");
break;
case 0x8664:
cli_dbgmsg("Machine type: AMD64\n");
break;
default:
cli_warnmsg("Unknown machine type in PE header (0x%x)\n", EC16(file_hdr.Machine));
}
//获取块的数目
nsections = EC16(file_hdr.NumberOfSections);
if(nsections < 1 || nsections > 96) {
if(DETECT_BROKEN) {
if(ctx->virname)
*ctx->virname = "Broken.Executable";
return CL_VIRUS;
}
if(nsections)
cli_warnmsg("PE file contains %d sections\n", nsections);
else
cli_warnmsg("PE file contains no sections\n");
return CL_CLEAN;
}
cli_dbgmsg("NumberOfSections: %d\n", nsections);
//获取文件创建时间
//自1970/1/1 用GMT计算的秒数
timestamp = (time_t) EC32(file_hdr.TimeDateStamp);
cli_dbgmsg("TimeDateStamp: %s", cli_ctime(×tamp, timestr, sizeof(timestr)));
//读取可选镜像文件头大小
cli_dbgmsg("SizeOfOptionalHeader: %x\n", EC16(file_hdr.SizeOfOptionalHeader));
//可选镜像文件头太小
if (EC16(file_hdr.SizeOfOptionalHeader) < sizeof(struct pe_image_optional_hdr32)) {
cli_dbgmsg("SizeOfOptionalHeader too small\n");
if(DETECT_BROKEN) {
if(ctx->virname)
*ctx->virname = "Broken.Executable";
return CL_VIRUS;
}
return CL_CLEAN;
}
//读取可选镜像文件头数据
if(cli_readn(desc, &optional_hdr32, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32)) {
cli_dbgmsg("Can't read optional file header\n");
if(DETECT_BROKEN) {
if(ctx->virname)
*ctx->virname = "Broken.Executable";
return CL_VIRUS;
}
return CL_CLEAN;
}
//读取可选镜像文件头标志,判断是32位还是64位
//ROM映像0x107
//普通可执行的映像0x10b
//PE32+是0x20b
/* This will be a chicken and egg problem until we drop 9x */
if(EC32(optional_hdr64.Magic)==PE32P_SIGNATURE) {
//再判断读取可选镜像文件头大小是否正确
if(EC16(file_hdr.SizeOfOptionalHeader)!=sizeof(struct pe_image_optional_hdr64)) {
/* FIXME: need to play around a bit more with xp64 */
cli_dbgmsg("Incorrect SizeOfOptionalHeader for PE32+\n");
if(DETECT_BROKEN) {
if(ctx->virname)
*ctx->virname = "Broken.Executable";
return CL_VIRUS;
}
return CL_CLEAN;
}
pe_plus = 1;
} else {
/*
either it's got a PE32_SIGNATURE or
we enable win9x compatibility in that we don't honor magic (see bb#119)
either way it's a 32bit thingy
*/
if(EC16(optional_hdr32.Magic) != PE32_SIGNATURE) {
cli_warnmsg("Incorrect magic number in optional header\n");
if(DETECT_BROKEN) {
if(ctx->virname)
*ctx->virname = "Broken.Executable";
return CL_VIRUS;
}
cli_dbgmsg("9x compatibility mode\n");
}
}
//非64位,既是32位
if(!pe_plus) { /* PE */
if (EC16(file_hdr.SizeOfOptionalHeader)!=sizeof(struct pe_image_optional_hdr32)) {
/* Seek to the end of the long header */
//略过多余部分数据
lseek(desc, (EC16(file_hdr.SizeOfOptionalHeader)-sizeof(struct pe_image_optional_hdr32)), SEEK_CUR);
}
//判断PE是否需要脱壳?
if(DCONF & PE_CONF_UPACK)
upack = (EC16(file_hdr.SizeOfOptionalHeader)==0x148);
//获取入口RVA
vep = EC32(optional_hdr32.AddressOfEntryPoint);
//获取MS-DOS头、PE文件头、区块表的组合大小
//域值四舍五入到文件对齐的倍数
hdr_size = EC32(optional_hdr32.SizeOfHeaders);
cli_dbgmsg("File format: PE\n");
cli_dbgmsg("MajorLinkerVersion: %d\n", optional_hdr32.MajorLinkerVersion);
cli_dbgmsg("MinorLinkerVersion: %d\n", optional_hdr32.MinorLinkerVersion);
cli_dbgmsg("SizeOfCode: 0x%x\n", EC32(optional_hdr32.SizeOfCode));
cli_dbgmsg("SizeOfInitializedData: 0x%x\n", EC32(optional_hdr32.SizeOfInitializedData));
cli_dbgmsg("SizeOfUninitializedData: 0x%x\n", EC32(optional_hdr32.SizeOfUninitializedData));
cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", vep);
cli_dbgmsg("BaseOfCode: 0x%x\n", EC32(optional_hdr32.BaseOfCode));
cli_dbgmsg("SectionAlignment: 0x%x\n", EC32(optional_hdr32.SectionAlignment));
cli_dbgmsg("FileAlignment: 0x%x\n", EC32(optional_hdr32.FileAlignment));
cli_dbgmsg("MajorSubsystemVersion: %d\n", EC16(optional_hdr32.MajorSubsystemVersion));
cli_dbgmsg("MinorSubsystemVersion: %d\n", EC16(optional_hdr32.MinorSubsystemVersion));
cli_dbgmsg("SizeOfImage: 0x%x\n", EC32(optional_hdr32.SizeOfImage));
cli_dbgmsg("SizeOfHeaders: 0x%x\n", hdr_size);
cli_dbgmsg("NumberOfRvaAndSizes: %d\n", EC32(optional_hdr32.NumberOfRvaAndSizes));
} else { /* PE+ */
//64位
/* read the remaining part of the header */
if(cli_readn(desc, &optional_hdr32 + 1, sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32)) {
cli_dbgmsg("Can't read optional file header\n");
if(DETECT_BROKEN) {
if(ctx->virname)
*ctx->virname = "Broken.Executable";
return CL_VIRUS;
}
return CL_CLEAN;
}
vep = EC32(optional_hdr64.AddressOfEntryPoint);
hdr_size = EC32(optional_hdr64.SizeOfHeaders);
cli_dbgmsg("File format: PE32+\n");
cli_dbgmsg("MajorLinkerVersion: %d\n", optional_hdr64.MajorLinkerVersion);
cli_dbgmsg("MinorLinkerVersion: %d\n", optional_hdr64.MinorLinkerVersion);
cli_dbgmsg("SizeOfCode: 0x%x\n", EC32(optional_hdr64.SizeOfCode));
cli_dbgmsg("SizeOfInitializedData: 0x%x\n", EC32(optional_hdr64.SizeOfInitializedData));
cli_dbgmsg("SizeOfUninitializedData: 0x%x\n", EC32(optional_hdr64.SizeOfUninitializedData));
cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", vep);
cli_dbgmsg("BaseOfCode: 0x%x\n", EC32(optional_hdr64.BaseOfCode));
cli_dbgmsg("SectionAlignment: 0x%x\n", EC32(optional_hdr64.SectionAlignment));
cli_dbgmsg("FileAlignment: 0x%x\n", EC32(optional_hdr64.FileAlignment));
cli_dbgmsg("MajorSubsystemVersion: %d\n", EC16(optional_hdr64.MajorSubsystemVersion));
cli_dbgmsg("MinorSubsystemVersion: %d\n", EC16(optional_hdr64.MinorSubsystemVersion));
cli_dbgmsg("SizeOfImage: 0x%x\n", EC32(optional_hdr64.SizeOfImage));
cli_dbgmsg("SizeOfHeaders: 0x%x\n", hdr_size);
cli_dbgmsg("NumberOfRvaAndSizes: %d\n", EC32(optional_hdr64.NumberOfRvaAndSizes));
}
//判断该执行文件所期待的子系统(用户界面类型)的枚举值
switch(pe_plus ? EC16(optional_hdr64.Subsystem) : EC16(optional_hdr32.Subsystem)) {
case 0:
cli_dbgmsg("Subsystem: Unknown\n");
break;
case 1:
cli_dbgmsg("Subsystem: Native (svc)\n");
native = 1;
break;
case 2:
cli_dbgmsg("Subsystem: Win32 GUI\n");
break;
case 3:
cli_dbgmsg("Subsystem: Win32 console\n");
break;
case 5:
cli_dbgmsg("Subsystem: OS/2 console\n");
break;
case 7:
cli_dbgmsg("Subsystem: POSIX console\n");
break;
case 8:
cli_dbgmsg("Subsystem: Native Win9x driver\n");
break;
case 9:
cli_dbgmsg("Subsystem: WinCE GUI\n");
break;
case 10:
cli_dbgmsg("Subsystem: EFI application\n");
break;
case 11:
cli_dbgmsg("Subsystem: EFI driver\n");
break;
case 12: