Browse code

sigtool: properly generates and reports pe section hashes (mdb)

Kevin Lin authored on 2016/06/30 07:21:39
Showing 4 changed files
... ...
@@ -174,6 +174,7 @@ CLAMAV_PRIVATE {
174 174
     cli_scanbuff;
175 175
     cli_fmap_scandesc;
176 176
     cli_checkfp_pe;
177
+    cli_genhash_pe;
177 178
     html_screnc_decode;
178 179
     mpool_create;
179 180
     mpool_calloc;
... ...
@@ -5444,3 +5444,190 @@ int cli_checkfp_pe(cli_ctx *ctx, uint8_t *authsha1, stats_section_t *hashes, uin
5444 5444
         return CL_VIRUS;
5445 5445
     }
5446 5446
 }
5447
+
5448
+int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type)
5449
+{
5450
+    uint16_t e_magic; /* DOS signature ("MZ") */
5451
+    uint16_t nsections;
5452
+    uint32_t e_lfanew; /* address of new exe header */
5453
+    union {
5454
+        struct pe_image_optional_hdr64 opt64;
5455
+        struct pe_image_optional_hdr32 opt32;
5456
+    } pe_opt;
5457
+    const struct pe_image_section_hdr *section_hdr;
5458
+    ssize_t at;
5459
+    unsigned int i, j, pe_plus = 0;
5460
+    size_t fsize;
5461
+    uint32_t valign, falign, hdr_size;
5462
+    struct pe_image_file_hdr file_hdr;
5463
+    struct cli_exe_section *exe_sections;
5464
+    struct pe_image_data_dir *dirs;
5465
+    fmap_t *map = *ctx->fmap;
5466
+
5467
+    if (class >= CL_GENHASH_PE_CLASS_LAST)
5468
+        return CL_EARG;
5469
+
5470
+    if(fmap_readn(map, &e_magic, 0, sizeof(e_magic)) != sizeof(e_magic))
5471
+        return CL_EFORMAT;
5472
+
5473
+    if(EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE && EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE_OLD)
5474
+        return CL_EFORMAT;
5475
+
5476
+    if(fmap_readn(map, &e_lfanew, 58 + sizeof(e_magic), sizeof(e_lfanew)) != sizeof(e_lfanew))
5477
+        return CL_EFORMAT;
5478
+
5479
+    e_lfanew = EC32(e_lfanew);
5480
+    if(!e_lfanew)
5481
+        return CL_EFORMAT;
5482
+
5483
+    if(fmap_readn(map, &file_hdr, e_lfanew, sizeof(struct pe_image_file_hdr)) != sizeof(struct pe_image_file_hdr))
5484
+        return CL_EFORMAT;
5485
+
5486
+    if(EC32(file_hdr.Magic) != PE_IMAGE_NT_SIGNATURE)
5487
+        return CL_EFORMAT;
5488
+
5489
+    nsections = EC16(file_hdr.NumberOfSections);
5490
+    if(nsections < 1 || nsections > 96)
5491
+        return CL_EFORMAT;
5492
+
5493
+    if(EC16(file_hdr.SizeOfOptionalHeader) < sizeof(struct pe_image_optional_hdr32))
5494
+        return CL_EFORMAT;
5495
+
5496
+    at = e_lfanew + sizeof(struct pe_image_file_hdr);
5497
+    if(fmap_readn(map, &optional_hdr32, at, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32))
5498
+        return CL_EFORMAT;
5499
+
5500
+    at += sizeof(struct pe_image_optional_hdr32);
5501
+
5502
+    /* This will be a chicken and egg problem until we drop 9x */
5503
+    if(EC16(optional_hdr64.Magic)==PE32P_SIGNATURE) {
5504
+        if(EC16(file_hdr.SizeOfOptionalHeader)!=sizeof(struct pe_image_optional_hdr64))
5505
+            return CL_EFORMAT;
5506
+
5507
+        pe_plus = 1;
5508
+    }
5509
+
5510
+    if(!pe_plus) { /* PE */
5511
+        if (EC16(file_hdr.SizeOfOptionalHeader)!=sizeof(struct pe_image_optional_hdr32)) {
5512
+            /* Seek to the end of the long header */
5513
+            at += EC16(file_hdr.SizeOfOptionalHeader)-sizeof(struct pe_image_optional_hdr32);
5514
+        }
5515
+
5516
+        hdr_size = EC32(optional_hdr32.SizeOfHeaders);
5517
+        dirs = optional_hdr32.DataDirectory;
5518
+    } else { /* PE+ */
5519
+        size_t readlen = sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32);
5520
+        /* read the remaining part of the header */
5521
+        if((size_t)fmap_readn(map, &optional_hdr32 + 1, at, readlen) != readlen)
5522
+            return CL_EFORMAT;
5523
+
5524
+        at += sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32);
5525
+        hdr_size = EC32(optional_hdr64.SizeOfHeaders);
5526
+        dirs = optional_hdr64.DataDirectory;
5527
+    }
5528
+
5529
+    fsize = map->len;
5530
+
5531
+    valign = (pe_plus)?EC32(optional_hdr64.SectionAlignment):EC32(optional_hdr32.SectionAlignment);
5532
+    falign = (pe_plus)?EC32(optional_hdr64.FileAlignment):EC32(optional_hdr32.FileAlignment);
5533
+
5534
+    section_hdr = fmap_need_off_once(map, at, sizeof(*section_hdr) * nsections);
5535
+    if(!section_hdr)
5536
+        return CL_EFORMAT;
5537
+
5538
+    at += sizeof(*section_hdr) * nsections;
5539
+
5540
+    exe_sections = (struct cli_exe_section *) cli_calloc(nsections, sizeof(struct cli_exe_section));
5541
+    if(!exe_sections)
5542
+        return CL_EMEM;
5543
+
5544
+    for(i = 0; falign!=0x200 && i<nsections; i++) {
5545
+        /* file alignment fallback mode - blah */
5546
+        if (falign && section_hdr[i].SizeOfRawData && EC32(section_hdr[i].PointerToRawData)%falign && !(EC32(section_hdr[i].PointerToRawData)%0x200))
5547
+            falign = 0x200;
5548
+    }
5549
+
5550
+    hdr_size = PESALIGN(hdr_size, falign); /* Aligned headers virtual size */
5551
+
5552
+    for(i = 0; i < nsections; i++) {
5553
+        exe_sections[i].rva = PEALIGN(EC32(section_hdr[i].VirtualAddress), valign);
5554
+        exe_sections[i].vsz = PESALIGN(EC32(section_hdr[i].VirtualSize), valign);
5555
+        exe_sections[i].raw = PEALIGN(EC32(section_hdr[i].PointerToRawData), falign);
5556
+        exe_sections[i].rsz = PESALIGN(EC32(section_hdr[i].SizeOfRawData), falign);
5557
+
5558
+        if (!exe_sections[i].vsz && exe_sections[i].rsz)
5559
+            exe_sections[i].vsz=PESALIGN(exe_sections[i].ursz, valign);
5560
+
5561
+        if (exe_sections[i].rsz && fsize>exe_sections[i].raw && !CLI_ISCONTAINED(0, (uint32_t) fsize, exe_sections[i].raw, exe_sections[i].rsz))
5562
+            exe_sections[i].rsz = fsize - exe_sections[i].raw;
5563
+
5564
+        if (exe_sections[i].rsz && exe_sections[i].raw >= fsize) {
5565
+            free(exe_sections);
5566
+            return CL_EFORMAT;
5567
+        }
5568
+
5569
+        if (exe_sections[i].urva>>31 || exe_sections[i].uvsz>>31 || (exe_sections[i].rsz && exe_sections[i].uraw>>31) || exe_sections[i].ursz>>31) {
5570
+            free(exe_sections);
5571
+            return CL_EFORMAT;
5572
+        }
5573
+    }
5574
+
5575
+    cli_qsort(exe_sections, nsections, sizeof(*exe_sections), sort_sects);
5576
+
5577
+    if (class == CL_GENHASH_PE_CLASS_SECTION) {
5578
+        unsigned char *hash, *hashset[CLI_HASH_AVAIL_TYPES], hstr[2*CLI_HASHLEN_MAX+1];
5579
+        int foundsize[CLI_HASH_AVAIL_TYPES];
5580
+        int foundwild[CLI_HASH_AVAIL_TYPES];
5581
+        int hlen = 0;
5582
+        int ret = CL_CLEAN;
5583
+
5584
+        /* pick hashtypes to generate */
5585
+        memset(foundsize, 0, sizeof(foundsize));
5586
+        memset(foundwild, 0, sizeof(foundwild));
5587
+        switch(type) {
5588
+        case 1:
5589
+            foundsize[CLI_HASH_MD5] = 1;
5590
+            hash = hashset[CLI_HASH_MD5] = cli_malloc(hashlen[CLI_HASH_MD5]);
5591
+            hlen = hashlen[CLI_HASH_MD5];
5592
+            break;
5593
+        case 2:
5594
+            foundsize[CLI_HASH_SHA1] = 1;
5595
+            hash = hashset[CLI_HASH_SHA1] = cli_malloc(hashlen[CLI_HASH_SHA1]);
5596
+            hlen = hashlen[CLI_HASH_SHA1];
5597
+            break;
5598
+        default:
5599
+            foundsize[CLI_HASH_SHA256] = 1;
5600
+            hash = hashset[CLI_HASH_SHA256] = cli_malloc(hashlen[CLI_HASH_SHA256]);
5601
+            hlen = hashlen[CLI_HASH_SHA256];
5602
+            break;
5603
+        }
5604
+
5605
+        if(!hash) {
5606
+            cli_errmsg("cli_genhash_pe: cli_malloc failed!\n");
5607
+            free(exe_sections);
5608
+            return CL_EMEM;
5609
+        }
5610
+
5611
+        for (i = 0; i < nsections; i++) {
5612
+            /* Generate hashes */
5613
+            cli_hashsect(*ctx->fmap, &exe_sections[i], hashset, foundsize, foundwild);
5614
+            for (j = 0; j < hlen; j++)
5615
+                snprintf(hstr+(2*j), sizeof(hstr)-(2*j), "%02x", hash[j]);
5616
+            hstr[j] = '\0';
5617
+            cli_dbgmsg("Section{%u}: %u:%s\n", i, exe_sections[i].rsz, hstr);
5618
+
5619
+            cli_dbgmsg("Section{%u}: %u:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
5620
+                       i, exe_sections[i].rsz, hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7],
5621
+                       hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]);
5622
+        }
5623
+
5624
+        free(hash);
5625
+    } else if (class == CL_GENHASH_PE_CLASS_IMPTBL) {
5626
+        /* TODO */
5627
+    } else {
5628
+        cli_dbgmsg("cli_genhash_pe: unknown pe genhash class: %u\n", class);
5629
+    }
5630
+
5631
+    free(exe_sections);
5632
+    return CL_SUCCESS;
5633
+}
... ...
@@ -165,8 +165,16 @@ int cli_scanpe(cli_ctx *ctx);
165 165
 #define CL_CHECKFP_PE_FLAG_STATS            0x00000001
166 166
 #define CL_CHECKFP_PE_FLAG_AUTHENTICODE     0x00000002
167 167
 
168
+enum {
169
+    CL_GENHASH_PE_CLASS_SECTION,
170
+    CL_GENHASH_PE_CLASS_IMPTBL,
171
+    /* place new class types above this line */
172
+    CL_GENHASH_PE_CLASS_LAST
173
+};
174
+
168 175
 int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo);
169 176
 int cli_checkfp_pe(cli_ctx *ctx, uint8_t *authsha1, stats_section_t *hashes, uint32_t flags);
177
+int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type);
170 178
 
171 179
 uint32_t cli_rawaddr(uint32_t, const struct cli_exe_section *, uint16_t, unsigned int *, size_t, uint32_t);
172 180
 void findres(uint32_t, uint32_t, uint32_t, fmap_t *map, struct cli_exe_section *, uint16_t, uint32_t, int (*)(void *, uint32_t, uint32_t, uint32_t, uint32_t), void *);
... ...
@@ -164,7 +164,120 @@ static int hexdump(void)
164 164
     return 0;
165 165
 }
166 166
 
167
-static int hashsig(const struct optstruct *opts, unsigned int mdb, int type)
167
+static int hashpe(const char *filename, unsigned int class, int type)
168
+{
169
+    STATBUF sb;
170
+    const char *fmptr;
171
+    struct cl_engine *engine;
172
+    cli_ctx ctx;
173
+    int fd, ret;
174
+
175
+    /* build engine */
176
+    if(!(engine = cl_engine_new())) {
177
+	mprintf("!hashpe: Can't create new engine\n");
178
+	return -1;
179
+    }
180
+    cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
181
+
182
+    if(cli_initroots(engine, 0) != CL_SUCCESS) {
183
+	mprintf("!hashpe: cli_initroots() failed\n");
184
+	cl_engine_free(engine);
185
+	return -1;
186
+    }
187
+
188
+    if(cli_parse_add(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
189
+	mprintf("!hashpe: Can't parse signature\n");
190
+	cl_engine_free(engine);
191
+	return -1;
192
+    }
193
+
194
+    if(cl_engine_compile(engine) != CL_SUCCESS) {
195
+	mprintf("!hashpe: Can't compile engine\n");
196
+	cl_engine_free(engine);
197
+	return -1;
198
+    }
199
+
200
+    /* prepare context */
201
+    memset(&ctx, '\0', sizeof(cli_ctx));
202
+    ctx.engine = engine;
203
+    ctx.options = CL_SCAN_STDOPT;
204
+    ctx.container_type = CL_TYPE_ANY;
205
+    ctx.dconf = (struct cli_dconf *) engine->dconf;
206
+    ctx.fmap = calloc(sizeof(fmap_t *), 1);
207
+    if(!ctx.fmap) {
208
+	cl_engine_free(engine);
209
+	return -1;
210
+    }
211
+
212
+    /* Prepare file */
213
+    fd = open(filename, O_RDONLY);
214
+    if(fd < 0) {
215
+	mprintf("!hashpe: Can't open file %s!\n", filename);
216
+        cl_engine_free(engine);
217
+        return -1;
218
+    }
219
+
220
+    lseek(fd, 0, SEEK_SET);
221
+    FSTAT(fd, &sb);
222
+    if(!(*ctx.fmap = fmap(fd, 0, sb.st_size))) {
223
+	free(ctx.fmap);
224
+	close(fd);
225
+	cl_engine_free(engine);
226
+	return -1;
227
+    }
228
+
229
+    fmptr = fmap_need_off_once(*ctx.fmap, 0, sb.st_size);
230
+    if(!fmptr) {
231
+        mprintf("!hashpe: fmap_need_off_once failed!\n");
232
+        free(ctx.fmap);
233
+        close(fd);
234
+        cl_engine_free(engine);
235
+	return -1;
236
+    }
237
+
238
+    cl_debug();
239
+
240
+    /* Send to PE-specific hasher */
241
+    switch(class) {
242
+        case 1:
243
+	    ret = cli_genhash_pe(&ctx, CL_GENHASH_PE_CLASS_SECTION, type);
244
+	    break;
245
+        case 2:
246
+	    ret = cli_genhash_pe(&ctx, CL_GENHASH_PE_CLASS_IMPTBL, type);
247
+	    break;
248
+        default:
249
+	    mprintf("!hashpe: unknown classification(%u) for pe hash!\n", class);
250
+	    return -1;
251
+    }
252
+
253
+    cli_debug_flag = 0;
254
+
255
+    /* THIS MAY BE UNNECESSARY */
256
+    switch(ret) {
257
+        case CL_CLEAN:
258
+            break;
259
+        case CL_VIRUS:
260
+            mprintf("*hashpe: CL_VIRUS after cli_genhash_pe()!\n");
261
+            break;
262
+        case CL_BREAK:
263
+            mprintf("*hashpe: CL_BREAK after cli_genhash_pe()!\n");
264
+            break;
265
+        case CL_EFORMAT:
266
+            mprintf("!hashpe: Not a valid PE file!\n");
267
+            break;
268
+        default:
269
+            mprintf("!hashpe: Other error %d inside cli_genhash_pe.\n", ret);
270
+            break;
271
+    }
272
+
273
+    /* Cleanup */
274
+    free(ctx.fmap);
275
+    close(fd);
276
+    cl_engine_free(engine);
277
+    return 0;
278
+}
279
+
280
+static int hashsig(const struct optstruct *opts, unsigned int class, int type)
168 281
 {
169 282
 	char *hash;
170 283
 	unsigned int i;
... ...
@@ -179,12 +292,11 @@ static int hashsig(const struct optstruct *opts, unsigned int mdb, int type)
179 179
 		return -1;
180 180
 	    } else {
181 181
 		if((sb.st_mode & S_IFMT) == S_IFREG) {
182
-		    if((hash = cli_hashfile(opts->filename[i], type))) {
183
-			if(mdb)
184
-			    mprintf("%u:%s:%s\n", (unsigned int) sb.st_size, hash, basename(opts->filename[i]));
185
-			else
186
-			    mprintf("%s:%u:%s\n", hash, (unsigned int) sb.st_size, basename(opts->filename[i]));
182
+		    if((class == 0) && (hash = cli_hashfile(opts->filename[i], type))) {
183
+			mprintf("%s:%u:%s\n", hash, (unsigned int) sb.st_size, basename(opts->filename[i]));
187 184
 			free(hash);
185
+		    } else if((class > 0) && (hashpe(opts->filename[i], class, type) == 0)) {
186
+			/* intentionally empty - printed in cli_genhash_pe() */
188 187
 		    } else {
189 188
 			mprintf("!hashsig: Can't generate hash for %s\n", opts->filename[i]);
190 189
 			return -1;
... ...
@@ -194,6 +306,10 @@ static int hashsig(const struct optstruct *opts, unsigned int mdb, int type)
194 194
 	}
195 195
 
196 196
     } else { /* stream */
197
+	if (class > 0) {
198
+	    mprintf("!hashsig: Can't generate requested hash for input stream\n");
199
+	    return -1;
200
+	}
197 201
 	hash = cli_hashstream(stdin, NULL, type);
198 202
 	if(!hash) {
199 203
 	    mprintf("!hashsig: Can't generate hash for input stream\n");