... | ... |
@@ -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"); |