boreal/module/
pe.rs

1use std::collections::HashMap;
2
3use crate::memory::Region;
4use crate::regex::Regex;
5use object::{
6    coff::{CoffHeader, SymbolTable},
7    pe::{self, ImageDosHeader, ImageNtHeaders32, ImageNtHeaders64},
8    read::pe::{
9        DataDirectories, DelayLoadImportTable, ExportTable, ImageNtHeaders, ImageOptionalHeader,
10        ImageThunkData, ImportTable, ImportThunkList, ResourceDirectory,
11        ResourceDirectoryEntryData, ResourceNameOrId, RichHeaderInfo,
12    },
13    FileKind, LittleEndian as LE, StringTable,
14};
15
16use super::{
17    EvalContext, Module, ModuleData, ModuleDataMap, ScanContext, StaticValue, Type, Value,
18};
19
20mod debug;
21mod ord;
22#[cfg(feature = "authenticode")]
23mod signatures;
24pub mod utils;
25mod version_info;
26
27const MAX_PE_SECTIONS: usize = 96;
28const MAX_PE_IMPORTS: usize = 16384;
29const MAX_PE_EXPORTS: usize = 16384;
30const MAX_EXPORT_NAME_LENGTH: usize = 512;
31const MAX_IMPORT_DLL_NAME_LENGTH: usize = 256;
32const MAX_RESOURCES: usize = 65536;
33const MAX_NB_DATA_DIRECTORIES: usize = 32768;
34const MAX_NB_VERSION_INFOS: usize = 32768;
35
36/// `pe` module. Allows inspecting PE inputs.
37#[derive(Debug)]
38pub struct Pe;
39
40#[repr(u8)]
41enum ImportType {
42    Standard = 0b0001,
43    Delayed = 0b0010,
44}
45
46impl Module for Pe {
47    fn get_name(&self) -> &'static str {
48        "pe"
49    }
50
51    fn get_static_values(&self) -> HashMap<&'static str, StaticValue> {
52        #[allow(clippy::cast_possible_wrap)]
53        #[allow(clippy::large_stack_arrays)]
54        HashMap::from([
55            (
56                "MACHINE_UNKNOWN",
57                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_UNKNOWN.into()),
58            ),
59            (
60                "MACHINE_AM33",
61                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_AM33.into()),
62            ),
63            (
64                "MACHINE_AMD64",
65                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_AMD64.into()),
66            ),
67            (
68                "MACHINE_ARM",
69                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_ARM.into()),
70            ),
71            (
72                "MACHINE_ARMNT",
73                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_ARMNT.into()),
74            ),
75            (
76                "MACHINE_ARM64",
77                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_ARM64.into()),
78            ),
79            (
80                "MACHINE_EBC",
81                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_EBC.into()),
82            ),
83            (
84                "MACHINE_I386",
85                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_I386.into()),
86            ),
87            (
88                "MACHINE_IA64",
89                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_IA64.into()),
90            ),
91            (
92                "MACHINE_M32R",
93                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_M32R.into()),
94            ),
95            (
96                "MACHINE_MIPS16",
97                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_MIPS16.into()),
98            ),
99            (
100                "MACHINE_MIPSFPU",
101                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_MIPSFPU.into()),
102            ),
103            (
104                "MACHINE_MIPSFPU16",
105                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_MIPSFPU16.into()),
106            ),
107            (
108                "MACHINE_POWERPC",
109                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_POWERPC.into()),
110            ),
111            (
112                "MACHINE_POWERPCFP",
113                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_POWERPCFP.into()),
114            ),
115            (
116                "MACHINE_R4000",
117                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_R4000.into()),
118            ),
119            (
120                "MACHINE_SH3",
121                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_SH3.into()),
122            ),
123            (
124                "MACHINE_SH3DSP",
125                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_SH3DSP.into()),
126            ),
127            (
128                "MACHINE_SH4",
129                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_SH4.into()),
130            ),
131            (
132                "MACHINE_SH5",
133                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_SH5.into()),
134            ),
135            (
136                "MACHINE_THUMB",
137                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_THUMB.into()),
138            ),
139            (
140                "MACHINE_WCEMIPSV2",
141                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_WCEMIPSV2.into()),
142            ),
143            (
144                "MACHINE_TARGET_HOST",
145                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_TARGET_HOST.into()),
146            ),
147            (
148                "MACHINE_R3000",
149                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_R3000.into()),
150            ),
151            (
152                "MACHINE_R10000",
153                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_R10000.into()),
154            ),
155            (
156                "MACHINE_ALPHA",
157                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_ALPHA.into()),
158            ),
159            (
160                "MACHINE_SH3E",
161                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_SH3E.into()),
162            ),
163            (
164                "MACHINE_ALPHA64",
165                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_ALPHA64.into()),
166            ),
167            (
168                "MACHINE_AXP64",
169                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_AXP64.into()),
170            ),
171            (
172                "MACHINE_TRICORE",
173                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_TRICORE.into()),
174            ),
175            (
176                "MACHINE_CEF",
177                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_CEF.into()),
178            ),
179            (
180                "MACHINE_CEE",
181                StaticValue::Integer(pe::IMAGE_FILE_MACHINE_CEE.into()),
182            ),
183            (
184                "SUBSYSTEM_UNKNOWN",
185                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_UNKNOWN.into()),
186            ),
187            (
188                "SUBSYSTEM_NATIVE",
189                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_NATIVE.into()),
190            ),
191            (
192                "SUBSYSTEM_WINDOWS_GUI",
193                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_WINDOWS_GUI.into()),
194            ),
195            (
196                "SUBSYSTEM_WINDOWS_CUI",
197                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_WINDOWS_CUI.into()),
198            ),
199            (
200                "SUBSYSTEM_OS2_CUI",
201                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_OS2_CUI.into()),
202            ),
203            (
204                "SUBSYSTEM_POSIX_CUI",
205                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_POSIX_CUI.into()),
206            ),
207            (
208                "SUBSYSTEM_NATIVE_WINDOWS",
209                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_NATIVE_WINDOWS.into()),
210            ),
211            (
212                "SUBSYSTEM_WINDOWS_CE_GUI",
213                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_WINDOWS_CE_GUI.into()),
214            ),
215            (
216                "SUBSYSTEM_EFI_APPLICATION",
217                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_EFI_APPLICATION.into()),
218            ),
219            (
220                "SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER",
221                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER.into()),
222            ),
223            (
224                "SUBSYSTEM_EFI_RUNTIME_DRIVER",
225                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER.into()),
226            ),
227            (
228                "SUBSYSTEM_EFI_ROM_IMAGE",
229                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_EFI_ROM.into()),
230            ),
231            (
232                "SUBSYSTEM_XBOX",
233                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_XBOX.into()),
234            ),
235            (
236                "SUBSYSTEM_WINDOWS_BOOT_APPLICATION",
237                StaticValue::Integer(pe::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION.into()),
238            ),
239            (
240                "HIGH_ENTROPY_VA",
241                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA.into()),
242            ),
243            (
244                "DYNAMIC_BASE",
245                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE.into()),
246            ),
247            (
248                "FORCE_INTEGRITY",
249                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY.into()),
250            ),
251            (
252                "NX_COMPAT",
253                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_NX_COMPAT.into()),
254            ),
255            (
256                "NO_ISOLATION",
257                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_NO_ISOLATION.into()),
258            ),
259            (
260                "NO_SEH",
261                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_NO_SEH.into()),
262            ),
263            (
264                "NO_BIND",
265                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_NO_BIND.into()),
266            ),
267            (
268                "APPCONTAINER",
269                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_APPCONTAINER.into()),
270            ),
271            (
272                "WDM_DRIVER",
273                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_WDM_DRIVER.into()),
274            ),
275            (
276                "GUARD_CF",
277                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_GUARD_CF.into()),
278            ),
279            (
280                "TERMINAL_SERVER_AWARE",
281                StaticValue::Integer(pe::IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE.into()),
282            ),
283            (
284                "RELOCS_STRIPPED",
285                StaticValue::Integer(pe::IMAGE_FILE_RELOCS_STRIPPED.into()),
286            ),
287            (
288                "EXECUTABLE_IMAGE",
289                StaticValue::Integer(pe::IMAGE_FILE_EXECUTABLE_IMAGE.into()),
290            ),
291            (
292                "LINE_NUMS_STRIPPED",
293                StaticValue::Integer(pe::IMAGE_FILE_LINE_NUMS_STRIPPED.into()),
294            ),
295            (
296                "LOCAL_SYMS_STRIPPED",
297                StaticValue::Integer(pe::IMAGE_FILE_LOCAL_SYMS_STRIPPED.into()),
298            ),
299            (
300                "AGGRESIVE_WS_TRIM",
301                StaticValue::Integer(pe::IMAGE_FILE_AGGRESIVE_WS_TRIM.into()),
302            ),
303            (
304                "LARGE_ADDRESS_AWARE",
305                StaticValue::Integer(pe::IMAGE_FILE_LARGE_ADDRESS_AWARE.into()),
306            ),
307            (
308                "BYTES_REVERSED_LO",
309                StaticValue::Integer(pe::IMAGE_FILE_BYTES_REVERSED_LO.into()),
310            ),
311            (
312                "MACHINE_32BIT",
313                StaticValue::Integer(pe::IMAGE_FILE_32BIT_MACHINE.into()),
314            ),
315            (
316                "DEBUG_STRIPPED",
317                StaticValue::Integer(pe::IMAGE_FILE_DEBUG_STRIPPED.into()),
318            ),
319            (
320                "REMOVABLE_RUN_FROM_SWAP",
321                StaticValue::Integer(pe::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP.into()),
322            ),
323            (
324                "NET_RUN_FROM_SWAP",
325                StaticValue::Integer(pe::IMAGE_FILE_NET_RUN_FROM_SWAP.into()),
326            ),
327            ("SYSTEM", StaticValue::Integer(pe::IMAGE_FILE_SYSTEM.into())),
328            ("DLL", StaticValue::Integer(pe::IMAGE_FILE_DLL.into())),
329            (
330                "UP_SYSTEM_ONLY",
331                StaticValue::Integer(pe::IMAGE_FILE_UP_SYSTEM_ONLY.into()),
332            ),
333            (
334                "BYTES_REVERSED_HI",
335                StaticValue::Integer(pe::IMAGE_FILE_BYTES_REVERSED_HI.into()),
336            ),
337            (
338                "IMAGE_DIRECTORY_ENTRY_EXPORT",
339                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_EXPORT as i64),
340            ),
341            (
342                "IMAGE_DIRECTORY_ENTRY_IMPORT",
343                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_IMPORT as i64),
344            ),
345            (
346                "IMAGE_DIRECTORY_ENTRY_RESOURCE",
347                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_RESOURCE as i64),
348            ),
349            (
350                "IMAGE_DIRECTORY_ENTRY_EXCEPTION",
351                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_EXCEPTION as i64),
352            ),
353            (
354                "IMAGE_DIRECTORY_ENTRY_SECURITY",
355                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_SECURITY as i64),
356            ),
357            (
358                "IMAGE_DIRECTORY_ENTRY_BASERELOC",
359                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_BASERELOC as i64),
360            ),
361            (
362                "IMAGE_DIRECTORY_ENTRY_DEBUG",
363                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_DEBUG as i64),
364            ),
365            (
366                "IMAGE_DIRECTORY_ENTRY_ARCHITECTURE",
367                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_ARCHITECTURE as i64),
368            ),
369            (
370                "IMAGE_DIRECTORY_ENTRY_COPYRIGHT",
371                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_ARCHITECTURE as i64),
372            ),
373            (
374                "IMAGE_DIRECTORY_ENTRY_GLOBALPTR",
375                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_GLOBALPTR as i64),
376            ),
377            (
378                "IMAGE_DIRECTORY_ENTRY_TLS",
379                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_TLS as i64),
380            ),
381            (
382                "IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG",
383                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG as i64),
384            ),
385            (
386                "IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT",
387                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT as i64),
388            ),
389            (
390                "IMAGE_DIRECTORY_ENTRY_IAT",
391                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_IAT as i64),
392            ),
393            (
394                "IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT",
395                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT as i64),
396            ),
397            (
398                "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR",
399                StaticValue::Integer(pe::IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR as i64),
400            ),
401            (
402                "IMAGE_NT_OPTIONAL_HDR32_MAGIC",
403                StaticValue::Integer(pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC.into()),
404            ),
405            (
406                "IMAGE_NT_OPTIONAL_HDR64_MAGIC",
407                StaticValue::Integer(pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC.into()),
408            ),
409            (
410                "IMAGE_ROM_OPTIONAL_HDR_MAGIC",
411                StaticValue::Integer(pe::IMAGE_ROM_OPTIONAL_HDR_MAGIC.into()),
412            ),
413            (
414                "SECTION_NO_PAD",
415                StaticValue::Integer(pe::IMAGE_SCN_TYPE_NO_PAD.into()),
416            ),
417            (
418                "SECTION_CNT_CODE",
419                StaticValue::Integer(pe::IMAGE_SCN_CNT_CODE.into()),
420            ),
421            (
422                "SECTION_CNT_INITIALIZED_DATA",
423                StaticValue::Integer(pe::IMAGE_SCN_CNT_INITIALIZED_DATA.into()),
424            ),
425            (
426                "SECTION_CNT_UNINITIALIZED_DATA",
427                StaticValue::Integer(pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA.into()),
428            ),
429            (
430                "SECTION_LNK_OTHER",
431                StaticValue::Integer(pe::IMAGE_SCN_LNK_OTHER.into()),
432            ),
433            (
434                "SECTION_LNK_INFO",
435                StaticValue::Integer(pe::IMAGE_SCN_LNK_INFO.into()),
436            ),
437            (
438                "SECTION_LNK_REMOVE",
439                StaticValue::Integer(pe::IMAGE_SCN_LNK_REMOVE.into()),
440            ),
441            (
442                "SECTION_LNK_COMDAT",
443                StaticValue::Integer(pe::IMAGE_SCN_LNK_COMDAT.into()),
444            ),
445            (
446                "SECTION_NO_DEFER_SPEC_EXC",
447                StaticValue::Integer(pe::IMAGE_SCN_NO_DEFER_SPEC_EXC.into()),
448            ),
449            (
450                "SECTION_GPREL",
451                StaticValue::Integer(pe::IMAGE_SCN_GPREL.into()),
452            ),
453            (
454                "SECTION_MEM_FARDATA",
455                StaticValue::Integer(pe::IMAGE_SCN_MEM_FARDATA.into()),
456            ),
457            (
458                "SECTION_MEM_PURGEABLE",
459                StaticValue::Integer(pe::IMAGE_SCN_MEM_PURGEABLE.into()),
460            ),
461            (
462                "SECTION_MEM_16BIT",
463                StaticValue::Integer(pe::IMAGE_SCN_MEM_16BIT.into()),
464            ),
465            (
466                "SECTION_MEM_LOCKED",
467                StaticValue::Integer(pe::IMAGE_SCN_MEM_LOCKED.into()),
468            ),
469            (
470                "SECTION_MEM_PRELOAD",
471                StaticValue::Integer(pe::IMAGE_SCN_MEM_PRELOAD.into()),
472            ),
473            (
474                "SECTION_ALIGN_1BYTES",
475                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_1BYTES.into()),
476            ),
477            (
478                "SECTION_ALIGN_2BYTES",
479                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_2BYTES.into()),
480            ),
481            (
482                "SECTION_ALIGN_4BYTES",
483                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_4BYTES.into()),
484            ),
485            (
486                "SECTION_ALIGN_8BYTES",
487                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_8BYTES.into()),
488            ),
489            (
490                "SECTION_ALIGN_16BYTES",
491                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_16BYTES.into()),
492            ),
493            (
494                "SECTION_ALIGN_32BYTES",
495                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_32BYTES.into()),
496            ),
497            (
498                "SECTION_ALIGN_64BYTES",
499                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_64BYTES.into()),
500            ),
501            (
502                "SECTION_ALIGN_128BYTES",
503                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_128BYTES.into()),
504            ),
505            (
506                "SECTION_ALIGN_256BYTES",
507                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_256BYTES.into()),
508            ),
509            (
510                "SECTION_ALIGN_512BYTES",
511                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_512BYTES.into()),
512            ),
513            (
514                "SECTION_ALIGN_1024BYTES",
515                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_1024BYTES.into()),
516            ),
517            (
518                "SECTION_ALIGN_2048BYTES",
519                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_2048BYTES.into()),
520            ),
521            (
522                "SECTION_ALIGN_4096BYTES",
523                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_4096BYTES.into()),
524            ),
525            (
526                "SECTION_ALIGN_8192BYTES",
527                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_8192BYTES.into()),
528            ),
529            (
530                "SECTION_ALIGN_MASK",
531                StaticValue::Integer(pe::IMAGE_SCN_ALIGN_MASK.into()),
532            ),
533            (
534                "SECTION_LNK_NRELOC_OVFL",
535                StaticValue::Integer(pe::IMAGE_SCN_LNK_NRELOC_OVFL.into()),
536            ),
537            (
538                "SECTION_MEM_DISCARDABLE",
539                StaticValue::Integer(pe::IMAGE_SCN_MEM_DISCARDABLE.into()),
540            ),
541            (
542                "SECTION_MEM_NOT_CACHED",
543                StaticValue::Integer(pe::IMAGE_SCN_MEM_NOT_CACHED.into()),
544            ),
545            (
546                "SECTION_MEM_NOT_PAGED",
547                StaticValue::Integer(pe::IMAGE_SCN_MEM_NOT_PAGED.into()),
548            ),
549            (
550                "SECTION_MEM_SHARED",
551                StaticValue::Integer(pe::IMAGE_SCN_MEM_SHARED.into()),
552            ),
553            (
554                "SECTION_MEM_EXECUTE",
555                StaticValue::Integer(pe::IMAGE_SCN_MEM_EXECUTE.into()),
556            ),
557            (
558                "SECTION_MEM_READ",
559                StaticValue::Integer(pe::IMAGE_SCN_MEM_READ.into()),
560            ),
561            (
562                "SECTION_MEM_WRITE",
563                StaticValue::Integer(pe::IMAGE_SCN_MEM_WRITE.into()),
564            ),
565            (
566                "SECTION_SCALE_INDEX",
567                StaticValue::Integer(pe::IMAGE_SCN_SCALE_INDEX.into()),
568            ),
569            (
570                "RESOURCE_TYPE_CURSOR",
571                StaticValue::Integer(pe::RT_CURSOR.into()),
572            ),
573            (
574                "RESOURCE_TYPE_BITMAP",
575                StaticValue::Integer(pe::RT_BITMAP.into()),
576            ),
577            (
578                "RESOURCE_TYPE_ICON",
579                StaticValue::Integer(pe::RT_ICON.into()),
580            ),
581            (
582                "RESOURCE_TYPE_MENU",
583                StaticValue::Integer(pe::RT_MENU.into()),
584            ),
585            (
586                "RESOURCE_TYPE_DIALOG",
587                StaticValue::Integer(pe::RT_DIALOG.into()),
588            ),
589            (
590                "RESOURCE_TYPE_STRING",
591                StaticValue::Integer(pe::RT_STRING.into()),
592            ),
593            (
594                "RESOURCE_TYPE_FONTDIR",
595                StaticValue::Integer(pe::RT_FONTDIR.into()),
596            ),
597            (
598                "RESOURCE_TYPE_FONT",
599                StaticValue::Integer(pe::RT_FONT.into()),
600            ),
601            (
602                "RESOURCE_TYPE_ACCELERATOR",
603                StaticValue::Integer(pe::RT_ACCELERATOR.into()),
604            ),
605            (
606                "RESOURCE_TYPE_RCDATA",
607                StaticValue::Integer(pe::RT_RCDATA.into()),
608            ),
609            (
610                "RESOURCE_TYPE_MESSAGETABLE",
611                StaticValue::Integer(pe::RT_MESSAGETABLE.into()),
612            ),
613            (
614                "RESOURCE_TYPE_GROUP_CURSOR",
615                StaticValue::Integer(pe::RT_GROUP_CURSOR.into()),
616            ),
617            (
618                "RESOURCE_TYPE_GROUP_ICON",
619                StaticValue::Integer(pe::RT_GROUP_ICON.into()),
620            ),
621            (
622                "RESOURCE_TYPE_VERSION",
623                StaticValue::Integer(pe::RT_VERSION.into()),
624            ),
625            (
626                "RESOURCE_TYPE_DLGINCLUDE",
627                StaticValue::Integer(pe::RT_DLGINCLUDE.into()),
628            ),
629            (
630                "RESOURCE_TYPE_PLUGPLAY",
631                StaticValue::Integer(pe::RT_PLUGPLAY.into()),
632            ),
633            ("RESOURCE_TYPE_VXD", StaticValue::Integer(pe::RT_VXD.into())),
634            (
635                "RESOURCE_TYPE_ANICURSOR",
636                StaticValue::Integer(pe::RT_ANICURSOR.into()),
637            ),
638            (
639                "RESOURCE_TYPE_ANIICON",
640                StaticValue::Integer(pe::RT_ANIICON.into()),
641            ),
642            (
643                "RESOURCE_TYPE_HTML",
644                StaticValue::Integer(pe::RT_HTML.into()),
645            ),
646            (
647                "RESOURCE_TYPE_MANIFEST",
648                StaticValue::Integer(pe::RT_MANIFEST.into()),
649            ),
650            (
651                "IMAGE_DEBUG_TYPE_UNKNOWN",
652                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_UNKNOWN.into()),
653            ),
654            (
655                "IMAGE_DEBUG_TYPE_COFF",
656                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_COFF.into()),
657            ),
658            (
659                "IMAGE_DEBUG_TYPE_CODEVIEW",
660                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_CODEVIEW.into()),
661            ),
662            (
663                "IMAGE_DEBUG_TYPE_FPO",
664                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_FPO.into()),
665            ),
666            (
667                "IMAGE_DEBUG_TYPE_MISC",
668                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_MISC.into()),
669            ),
670            (
671                "IMAGE_DEBUG_TYPE_EXCEPTION",
672                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_EXCEPTION.into()),
673            ),
674            (
675                "IMAGE_DEBUG_TYPE_FIXUP",
676                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_FIXUP.into()),
677            ),
678            (
679                "IMAGE_DEBUG_TYPE_OMAP_TO_SRC",
680                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_OMAP_TO_SRC.into()),
681            ),
682            (
683                "IMAGE_DEBUG_TYPE_OMAP_FROM_SRC",
684                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC.into()),
685            ),
686            (
687                "IMAGE_DEBUG_TYPE_BORLAND",
688                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_BORLAND.into()),
689            ),
690            (
691                "IMAGE_DEBUG_TYPE_RESERVED10",
692                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_RESERVED10.into()),
693            ),
694            (
695                "IMAGE_DEBUG_TYPE_CLSID",
696                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_CLSID.into()),
697            ),
698            (
699                "IMAGE_DEBUG_TYPE_VC_FEATURE",
700                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_VC_FEATURE.into()),
701            ),
702            (
703                "IMAGE_DEBUG_TYPE_POGO",
704                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_POGO.into()),
705            ),
706            (
707                "IMAGE_DEBUG_TYPE_ILTCG",
708                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_ILTCG.into()),
709            ),
710            (
711                "IMAGE_DEBUG_TYPE_MPX",
712                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_MPX.into()),
713            ),
714            (
715                "IMAGE_DEBUG_TYPE_REPRO",
716                StaticValue::Integer(pe::IMAGE_DEBUG_TYPE_REPRO.into()),
717            ),
718            (
719                "IMPORT_DELAYED",
720                StaticValue::Integer((ImportType::Delayed as u8).into()),
721            ),
722            (
723                "IMPORT_STANDARD",
724                StaticValue::Integer((ImportType::Standard as u8).into()),
725            ),
726            ("IMPORT_ANY", StaticValue::Integer(!0)),
727            (
728                "section_index",
729                StaticValue::function(
730                    Self::section_index,
731                    vec![vec![Type::Bytes], vec![Type::Integer]],
732                    Type::Integer,
733                ),
734            ),
735            (
736                "exports",
737                StaticValue::function(
738                    Self::exports,
739                    vec![vec![Type::Bytes], vec![Type::Integer], vec![Type::Regex]],
740                    Type::Integer,
741                ),
742            ),
743            (
744                "exports_index",
745                StaticValue::function(
746                    Self::exports_index,
747                    vec![vec![Type::Bytes], vec![Type::Integer], vec![Type::Regex]],
748                    Type::Integer,
749                ),
750            ),
751            (
752                "imports",
753                StaticValue::function(
754                    Self::imports,
755                    vec![
756                        vec![Type::Bytes, Type::Bytes],
757                        vec![Type::Bytes, Type::Integer],
758                        vec![Type::Bytes],
759                        vec![Type::Regex, Type::Regex],
760                        vec![Type::Integer, Type::Bytes, Type::Bytes],
761                        vec![Type::Integer, Type::Bytes, Type::Integer],
762                        vec![Type::Integer, Type::Bytes],
763                        vec![Type::Integer, Type::Regex, Type::Regex],
764                    ],
765                    Type::Integer,
766                ),
767            ),
768            (
769                "import_rva",
770                StaticValue::function(
771                    Self::import_rva,
772                    vec![
773                        vec![Type::Bytes, Type::Bytes],
774                        vec![Type::Bytes, Type::Integer],
775                    ],
776                    Type::Integer,
777                ),
778            ),
779            (
780                "delayed_import_rva",
781                StaticValue::function(
782                    Self::delayed_import_rva,
783                    vec![
784                        vec![Type::Bytes, Type::Bytes],
785                        vec![Type::Bytes, Type::Integer],
786                    ],
787                    Type::Integer,
788                ),
789            ),
790            (
791                "locale",
792                StaticValue::function(Self::locale, vec![vec![Type::Integer]], Type::Integer),
793            ),
794            (
795                "language",
796                StaticValue::function(Self::language, vec![vec![Type::Integer]], Type::Integer),
797            ),
798            (
799                "is_dll",
800                StaticValue::function(Self::is_dll, vec![], Type::Integer),
801            ),
802            (
803                "is_32bit",
804                StaticValue::function(Self::is_32bit, vec![], Type::Integer),
805            ),
806            (
807                "is_64bit",
808                StaticValue::function(Self::is_64bit, vec![], Type::Integer),
809            ),
810            (
811                "calculate_checksum",
812                StaticValue::function(Self::calculate_checksum, vec![], Type::Integer),
813            ),
814            #[cfg(feature = "hash")]
815            (
816                "imphash",
817                StaticValue::function(Self::imphash, vec![], Type::Bytes),
818            ),
819            (
820                "rva_to_offset",
821                StaticValue::function(
822                    Self::rva_to_offset,
823                    vec![vec![Type::Integer]],
824                    Type::Integer,
825                ),
826            ),
827        ])
828    }
829
830    fn get_dynamic_types(&self) -> HashMap<&'static str, Type> {
831        [
832            ("is_pe", Type::Integer),
833            // File header
834            ("machine", Type::Integer),
835            ("number_of_sections", Type::Integer),
836            ("timestamp", Type::Integer),
837            ("pointer_to_symbol_table", Type::Integer),
838            ("number_of_symbols", Type::Integer),
839            ("size_of_optional_header", Type::Integer),
840            ("characteristics", Type::Integer),
841            //
842            ("entry_point", Type::Integer),
843            ("entry_point_raw", Type::Integer),
844            ("image_base", Type::Integer),
845            ("number_of_rva_and_sizes", Type::Integer),
846            ("number_of_version_infos", Type::Integer),
847            // version info
848            ("version_info", Type::dict(Type::Bytes)),
849            (
850                "version_info_list",
851                Type::array(Type::object([("key", Type::Bytes), ("value", Type::Bytes)])),
852            ),
853            // Optional header part 1
854            ("opthdr_magic", Type::Integer),
855            ("size_of_code", Type::Integer),
856            ("size_of_initialized_data", Type::Integer),
857            ("size_of_uninitialized_data", Type::Integer),
858            ("base_of_code", Type::Integer),
859            ("base_of_data", Type::Integer),
860            ("section_alignment", Type::Integer),
861            ("file_alignment", Type::Integer),
862            (
863                "linker_version",
864                Type::object([("major", Type::Integer), ("minor", Type::Integer)]),
865            ),
866            (
867                "os_version",
868                Type::object([("major", Type::Integer), ("minor", Type::Integer)]),
869            ),
870            (
871                "image_version",
872                Type::object([("major", Type::Integer), ("minor", Type::Integer)]),
873            ),
874            (
875                "subsystem_version",
876                Type::object([("major", Type::Integer), ("minor", Type::Integer)]),
877            ),
878            ("win32_version_value", Type::Integer),
879            ("size_of_image", Type::Integer),
880            ("size_of_headers", Type::Integer),
881            ("checksum", Type::Integer),
882            ("subsystem", Type::Integer),
883            ("dll_characteristics", Type::Integer),
884            ("size_of_stack_reserve", Type::Integer),
885            ("size_of_stack_commit", Type::Integer),
886            ("size_of_heap_reserve", Type::Integer),
887            ("size_of_heap_commit", Type::Integer),
888            ("loader_flags", Type::Integer),
889            //
890            (
891                "data_directories",
892                Type::array(Type::object([
893                    ("virtual_address", Type::Integer),
894                    ("size", Type::Integer),
895                ])),
896            ),
897            (
898                "sections",
899                Type::array(Type::object([
900                    ("name", Type::Bytes),
901                    ("full_name", Type::Bytes),
902                    ("characteristics", Type::Integer),
903                    ("virtual_address", Type::Integer),
904                    ("virtual_size", Type::Integer),
905                    ("raw_data_offset", Type::Integer),
906                    ("raw_data_size", Type::Integer),
907                    ("pointer_to_relocations", Type::Integer),
908                    ("pointer_to_line_numbers", Type::Integer),
909                    ("number_of_relocations", Type::Integer),
910                    ("number_of_line_numbers", Type::Integer),
911                ])),
912            ),
913            (
914                "overlay",
915                Type::object([("offset", Type::Integer), ("size", Type::Integer)]),
916            ),
917            (
918                "rich_signature",
919                Type::object([
920                    ("offset", Type::Integer),
921                    ("length", Type::Integer),
922                    ("key", Type::Integer),
923                    ("raw_data", Type::Bytes),
924                    ("clear_data", Type::Bytes),
925                    ("version_data", Type::Bytes),
926                    (
927                        "version",
928                        Type::function(
929                            vec![vec![Type::Integer], vec![Type::Integer, Type::Integer]],
930                            Type::Integer,
931                        ),
932                    ),
933                    (
934                        "toolid",
935                        Type::function(
936                            vec![vec![Type::Integer], vec![Type::Integer, Type::Integer]],
937                            Type::Integer,
938                        ),
939                    ),
940                ]),
941            ),
942            ("number_of_imports", Type::Integer),
943            ("number_of_imported_functions", Type::Integer),
944            ("number_of_delayed_imports", Type::Integer),
945            ("number_of_delayed_imported_functions", Type::Integer),
946            ("number_of_exports", Type::Integer),
947            //
948            ("dll_name", Type::Bytes),
949            ("export_timestamp", Type::Integer),
950            (
951                "export_details",
952                Type::array(Type::object([
953                    ("offset", Type::Integer),
954                    ("name", Type::Bytes),
955                    ("forward_name", Type::Bytes),
956                    ("ordinal", Type::Integer),
957                    ("rva", Type::Integer),
958                ])),
959            ),
960            (
961                "import_details",
962                Type::array(Type::object([
963                    ("library_name", Type::Bytes),
964                    ("number_of_functions", Type::Integer),
965                    (
966                        "functions",
967                        Type::array(Type::object([
968                            ("name", Type::Bytes),
969                            ("ordinal", Type::Integer),
970                            ("rva", Type::Integer),
971                        ])),
972                    ),
973                ])),
974            ),
975            (
976                "delayed_import_details",
977                Type::array(Type::object([
978                    ("library_name", Type::Bytes),
979                    ("number_of_functions", Type::Integer),
980                    (
981                        "functions",
982                        Type::array(Type::object([
983                            ("name", Type::Bytes),
984                            ("ordinal", Type::Integer),
985                            ("rva", Type::Integer),
986                        ])),
987                    ),
988                ])),
989            ),
990            ("resource_timestamp", Type::Integer),
991            (
992                "resource_version",
993                Type::object([("major", Type::Integer), ("minor", Type::Integer)]),
994            ),
995            (
996                "resources",
997                Type::array(Type::object([
998                    ("rva", Type::Integer),
999                    ("offset", Type::Integer),
1000                    ("length", Type::Integer),
1001                    ("type", Type::Integer),
1002                    ("id", Type::Integer),
1003                    ("language", Type::Integer),
1004                    ("type_string", Type::Bytes),
1005                    ("name_string", Type::Bytes),
1006                    ("language_string", Type::Bytes),
1007                ])),
1008            ),
1009            ("number_of_resources", Type::Integer),
1010            ("pdb_path", Type::Bytes),
1011            #[cfg(feature = "authenticode")]
1012            ("number_of_signatures", Type::Integer),
1013            #[cfg(feature = "authenticode")]
1014            ("is_signed", Type::Integer),
1015            #[cfg(feature = "authenticode")]
1016            (
1017                "signatures",
1018                Type::array(Type::object([
1019                    ("thumbprint", Type::Bytes),
1020                    ("issuer", Type::Bytes),
1021                    ("subject", Type::Bytes),
1022                    ("version", Type::Integer),
1023                    ("algorithm", Type::Bytes),
1024                    ("algorithm_oid", Type::Bytes),
1025                    ("serial", Type::Bytes),
1026                    ("not_before", Type::Integer),
1027                    ("not_after", Type::Integer),
1028                    ("verified", Type::Integer),
1029                    ("digest_alg", Type::Bytes),
1030                    ("digest", Type::Bytes),
1031                    ("file_digest", Type::Bytes),
1032                    ("number_of_certificates", Type::Integer),
1033                    (
1034                        "certificates",
1035                        Type::array(Type::object([
1036                            ("thumbprint", Type::Bytes),
1037                            ("issuer", Type::Bytes),
1038                            ("subject", Type::Bytes),
1039                            ("version", Type::Integer),
1040                            ("algorithm", Type::Bytes),
1041                            ("algorithm_oid", Type::Bytes),
1042                            ("serial", Type::Bytes),
1043                            ("not_before", Type::Integer),
1044                            ("not_after", Type::Integer),
1045                        ])),
1046                    ),
1047                    (
1048                        "signer_info",
1049                        Type::object([
1050                            ("program_name", Type::Bytes),
1051                            ("digest", Type::Bytes),
1052                            ("digest_alg", Type::Bytes),
1053                            ("length_of_chain", Type::Integer),
1054                            (
1055                                "chain",
1056                                Type::array(Type::object([
1057                                    ("thumbprint", Type::Bytes),
1058                                    ("issuer", Type::Bytes),
1059                                    ("subject", Type::Bytes),
1060                                    ("version", Type::Integer),
1061                                    ("algorithm", Type::Bytes),
1062                                    ("algorithm_oid", Type::Bytes),
1063                                    ("serial", Type::Bytes),
1064                                    ("not_before", Type::Integer),
1065                                    ("not_after", Type::Integer),
1066                                ])),
1067                            ),
1068                        ]),
1069                    ),
1070                    ("number_of_countersignatures", Type::Integer),
1071                    (
1072                        "countersignatures",
1073                        Type::array(Type::object([
1074                            ("verified", Type::Integer),
1075                            ("sign_time", Type::Integer),
1076                            ("digest", Type::Bytes),
1077                            ("digest_alg", Type::Bytes),
1078                            ("length_of_chain", Type::Integer),
1079                            (
1080                                "chain",
1081                                Type::array(Type::object([
1082                                    ("thumbprint", Type::Bytes),
1083                                    ("issuer", Type::Bytes),
1084                                    ("subject", Type::Bytes),
1085                                    ("version", Type::Integer),
1086                                    ("algorithm", Type::Bytes),
1087                                    ("algorithm_oid", Type::Bytes),
1088                                    ("serial", Type::Bytes),
1089                                    ("not_before", Type::Integer),
1090                                    ("not_after", Type::Integer),
1091                                ])),
1092                            ),
1093                        ])),
1094                    ),
1095                    (
1096                        "valid_on",
1097                        Type::function(vec![vec![Type::Integer]], Type::Integer),
1098                    ),
1099                ])),
1100            ),
1101        ]
1102        .into()
1103    }
1104
1105    fn setup_new_scan(&self, data_map: &mut ModuleDataMap) {
1106        data_map.insert::<Self>(Data::default());
1107    }
1108
1109    fn get_dynamic_values(&self, ctx: &mut ScanContext, out: &mut HashMap<&'static str, Value>) {
1110        let Some(data) = ctx.module_data.get_mut::<Self>() else {
1111            return;
1112        };
1113
1114        if data.found_pe {
1115            // We already found a PE in a region, so ignore the others
1116            return;
1117        }
1118
1119        let res = match FileKind::parse(ctx.region.mem) {
1120            Ok(FileKind::Pe32) => {
1121                data.is_32bit = true;
1122                parse_file::<ImageNtHeaders32>(ctx.region, ctx.process_memory, data)
1123            }
1124            Ok(FileKind::Pe64) => {
1125                data.is_32bit = false;
1126                parse_file::<ImageNtHeaders64>(ctx.region, ctx.process_memory, data)
1127            }
1128            _ => None,
1129        };
1130
1131        match res {
1132            Some(values) => {
1133                *out = values;
1134                data.found_pe = true;
1135            }
1136            None => *out = [("is_pe", 0.into())].into(),
1137        }
1138    }
1139}
1140
1141impl ModuleData for Pe {
1142    type PrivateData = Data;
1143    type UserData = ();
1144}
1145
1146fn parse_file<HEADERS: ImageNtHeaders>(
1147    region: &Region,
1148    process_memory: bool,
1149    data: &mut Data,
1150) -> Option<HashMap<&'static str, Value>> {
1151    let dos_header = ImageDosHeader::parse(region.mem).ok()?;
1152    let mut offset = dos_header.nt_headers_offset().into();
1153    let (nt_headers, data_dirs) = HEADERS::parse(region.mem, &mut offset).ok()?;
1154
1155    let sections = utils::SectionTable::new(nt_headers, region.mem, offset);
1156
1157    let hdr = nt_headers.file_header();
1158    let characteristics = hdr.characteristics.get(LE);
1159
1160    if process_memory && (characteristics & pe::IMAGE_FILE_DLL) != 0 {
1161        return None;
1162    }
1163
1164    let symbols = hdr.symbols(region.mem).ok();
1165
1166    let opt_hdr = nt_headers.optional_header();
1167    let ep = opt_hdr.address_of_entry_point();
1168
1169    // libyara does not return a bool, but the result of the bitwise and...
1170    data.is_dll = characteristics & pe::IMAGE_FILE_DLL;
1171
1172    let entrypoint: Value = if process_memory {
1173        let ep: Option<usize> = ep.try_into().ok();
1174        ep.and_then(|ep| ep.checked_add(region.start)).into()
1175    } else {
1176        sections
1177            .as_ref()
1178            .and_then(|sections| utils::va_to_file_offset(region.mem, sections, ep))
1179            .map_or(-1, i64::from)
1180            .into()
1181    };
1182
1183    let mut map: HashMap<_, _> = [
1184        ("is_pe", Value::Integer(1)),
1185        // File header
1186        ("machine", hdr.machine.get(LE).into()),
1187        ("number_of_sections", hdr.number_of_sections.get(LE).into()),
1188        ("timestamp", hdr.time_date_stamp.get(LE).into()),
1189        (
1190            "pointer_to_symbol_table",
1191            hdr.pointer_to_symbol_table.get(LE).into(),
1192        ),
1193        ("number_of_symbols", hdr.number_of_symbols.get(LE).into()),
1194        (
1195            "size_of_optional_header",
1196            hdr.size_of_optional_header.get(LE).into(),
1197        ),
1198        ("characteristics", characteristics.into()),
1199        //
1200        ("entry_point", entrypoint),
1201        ("entry_point_raw", ep.into()),
1202        ("image_base", opt_hdr.image_base().into()),
1203        (
1204            "number_of_rva_and_sizes",
1205            opt_hdr.number_of_rva_and_sizes().into(),
1206        ),
1207        // Optional header
1208        ("opthdr_magic", opt_hdr.magic().into()),
1209        ("size_of_code", opt_hdr.size_of_code().into()),
1210        (
1211            "size_of_initialized_data",
1212            opt_hdr.size_of_initialized_data().into(),
1213        ),
1214        (
1215            "size_of_uninitialized_data",
1216            opt_hdr.size_of_uninitialized_data().into(),
1217        ),
1218        ("base_of_code", opt_hdr.base_of_code().into()),
1219        ("base_of_data", opt_hdr.base_of_data().into()),
1220        ("section_alignment", opt_hdr.section_alignment().into()),
1221        ("file_alignment", opt_hdr.file_alignment().into()),
1222        (
1223            "linker_version",
1224            Value::object([
1225                ("major", opt_hdr.major_linker_version().into()),
1226                ("minor", opt_hdr.minor_linker_version().into()),
1227            ]),
1228        ),
1229        (
1230            "os_version",
1231            Value::object([
1232                ("major", opt_hdr.major_operating_system_version().into()),
1233                ("minor", opt_hdr.minor_operating_system_version().into()),
1234            ]),
1235        ),
1236        (
1237            "image_version",
1238            Value::object([
1239                ("major", opt_hdr.major_image_version().into()),
1240                ("minor", opt_hdr.minor_image_version().into()),
1241            ]),
1242        ),
1243        (
1244            "subsystem_version",
1245            Value::object([
1246                ("major", opt_hdr.major_subsystem_version().into()),
1247                ("minor", opt_hdr.minor_subsystem_version().into()),
1248            ]),
1249        ),
1250        ("win32_version_value", opt_hdr.win32_version_value().into()),
1251        ("size_of_image", opt_hdr.size_of_image().into()),
1252        ("size_of_headers", opt_hdr.size_of_headers().into()),
1253        ("checksum", opt_hdr.check_sum().into()),
1254        ("subsystem", opt_hdr.subsystem().into()),
1255        ("dll_characteristics", opt_hdr.dll_characteristics().into()),
1256        (
1257            "size_of_stack_reserve",
1258            opt_hdr.size_of_stack_reserve().into(),
1259        ),
1260        (
1261            "size_of_stack_commit",
1262            opt_hdr.size_of_stack_commit().into(),
1263        ),
1264        (
1265            "size_of_heap_reserve",
1266            opt_hdr.size_of_heap_reserve().into(),
1267        ),
1268        ("size_of_heap_commit", opt_hdr.size_of_heap_commit().into()),
1269        ("loader_flags", opt_hdr.loader_flags().into()),
1270        //
1271        ("data_directories", data_directories(data_dirs)),
1272        (
1273            "sections",
1274            sections.as_ref().map_or(Value::Undefined, |sections| {
1275                sections_to_value(sections, symbols.as_ref().map(SymbolTable::strings), data)
1276            }),
1277        ),
1278        (
1279            "overlay",
1280            sections
1281                .as_ref()
1282                .map_or(Value::Undefined, |sections| overlay(sections, region.mem)),
1283        ),
1284        (
1285            "pdb_path",
1286            sections
1287                .as_ref()
1288                .and_then(|sections| debug::pdb_path(&data_dirs, region.mem, sections))
1289                .unwrap_or(Value::Undefined),
1290        ),
1291        (
1292            "rich_signature",
1293            RichHeaderInfo::parse(region.mem, dos_header.nt_headers_offset().into()).map_or_else(
1294                || {
1295                    // Setting this is a bit useless, but it mirrors what yara does
1296                    // and helps comparing module values.
1297                    Value::object([
1298                        ("version", Value::function(Pe::rich_signature_version)),
1299                        ("toolid", Value::function(Pe::rich_signature_toolid)),
1300                    ])
1301                },
1302                |info| rich_signature(info, region, data),
1303            ),
1304        ),
1305        ("number_of_version_infos", 0.into()),
1306    ]
1307    .into();
1308
1309    if let Some(sections) = sections.as_ref() {
1310        add_imports::<HEADERS>(&data_dirs, region.mem, sections, data, &mut map);
1311        add_delay_load_imports::<HEADERS>(&data_dirs, region.mem, sections, data, &mut map);
1312        add_exports(&data_dirs, region.mem, sections, data, &mut map);
1313        add_resources(&data_dirs, region.mem, sections, data, &mut map);
1314    }
1315
1316    #[cfg(feature = "authenticode")]
1317    if let Some((signatures, is_signed)) = signatures::get_signatures(&data_dirs, region.mem) {
1318        let _r = map.insert("number_of_signatures", signatures.len().into());
1319        let _r = map.insert("is_signed", is_signed);
1320        let _r = map.insert("signatures", Value::Array(signatures));
1321    } else {
1322        let _r = map.insert("number_of_signatures", Value::Integer(0));
1323        let _r = map.insert("is_signed", Value::Integer(0));
1324    }
1325
1326    Some(map)
1327}
1328
1329fn rich_signature(info: RichHeaderInfo, region: &Region, data: &mut Data) -> Value {
1330    data.rich_entries = info
1331        .unmasked_entries()
1332        .map(|entry| DataRichEntry {
1333            version: (entry.comp_id & 0xFFFF) as u16,
1334            toolid: (entry.comp_id >> 16) as u16,
1335            times: entry.count,
1336        })
1337        .collect();
1338
1339    let length = info.length.saturating_sub(8);
1340
1341    let raw = if info.offset + length <= region.mem.len() {
1342        Some(region.mem[info.offset..(info.offset + length)].to_vec())
1343    } else {
1344        None
1345    };
1346    let xor_key_bytes = info.xor_key.to_le_bytes();
1347    // Xor raw with the xor_key, but 4 bytes by 4 bytes
1348    let clear = raw.clone().map(|mut clear| {
1349        for (b, k) in clear.iter_mut().zip(xor_key_bytes.iter().cycle()) {
1350            *b ^= k;
1351        }
1352        clear
1353    });
1354
1355    let mut version = Vec::new();
1356    for entry in info.unmasked_entries() {
1357        version.extend(entry.comp_id.to_le_bytes().as_slice());
1358    }
1359
1360    Value::object([
1361        ("offset", (region.start + info.offset).into()),
1362        ("length", length.into()),
1363        ("key", info.xor_key.into()),
1364        ("raw_data", raw.into()),
1365        ("clear_data", clear.into()),
1366        ("version_data", version.into()),
1367        ("version", Value::function(Pe::rich_signature_version)),
1368        ("toolid", Value::function(Pe::rich_signature_toolid)),
1369    ])
1370}
1371
1372fn add_imports<Pe: ImageNtHeaders>(
1373    data_dirs: &DataDirectories,
1374    mem: &[u8],
1375    sections: &utils::SectionTable,
1376    data: &mut Data,
1377    out: &mut HashMap<&'static str, Value>,
1378) {
1379    let mut imports = Vec::new();
1380    let mut nb_functions_total = 0;
1381
1382    let table = data_dirs
1383        .get(pe::IMAGE_DIRECTORY_ENTRY_IMPORT)
1384        .and_then(|dir| {
1385            let import_va = dir.virtual_address.get(LE);
1386            let (section_data, section_va) = sections.get_section_containing(mem, import_va)?;
1387            Some(ImportTable::new(section_data, section_va, import_va))
1388        });
1389    let descriptors = table.as_ref().and_then(|table| table.descriptors().ok());
1390    if let (Some(table), Some(mut descriptors)) = (table, descriptors) {
1391        while let Ok(Some(import_desc)) = descriptors.next() {
1392            let library = match table.name(import_desc.name.get(LE)) {
1393                Ok(name) => name.to_vec(),
1394                Err(_) => continue,
1395            };
1396            if library.len() >= MAX_IMPORT_DLL_NAME_LENGTH || !dll_name_is_valid(&library) {
1397                continue;
1398            }
1399
1400            let mut data_functions = Vec::new();
1401            let import_thunk_list = table
1402                .thunks(import_desc.original_first_thunk.get(LE))
1403                .or_else(|_| table.thunks(import_desc.first_thunk.get(LE)));
1404            let functions = import_thunk_list.ok().map(|mut thunks| {
1405                import_functions::<Pe, _>(
1406                    &mut thunks,
1407                    &library,
1408                    |hint| table.hint_name(hint).map(|(_, name)| name.to_vec()),
1409                    import_desc.first_thunk.get(LE),
1410                    data.is_32bit,
1411                    &mut data_functions,
1412                    &mut nb_functions_total,
1413                )
1414            });
1415            if functions.as_ref().map_or(true, Vec::is_empty) {
1416                continue;
1417            }
1418
1419            data.imports.push(DataImport {
1420                dll_name: library.clone(),
1421                functions: data_functions,
1422            });
1423
1424            imports.push(Value::object([
1425                ("library_name", library.into()),
1426                (
1427                    "number_of_functions",
1428                    functions.as_ref().map(Vec::len).into(),
1429                ),
1430                (
1431                    "functions",
1432                    functions.map_or(Value::Undefined, Value::Array),
1433                ),
1434            ]));
1435            if imports.len() >= MAX_PE_IMPORTS {
1436                break;
1437            }
1438        }
1439    }
1440
1441    out.extend([
1442        ("number_of_imported_functions", nb_functions_total.into()),
1443        ("number_of_imports", imports.len().into()),
1444        ("import_details", Value::Array(imports)),
1445    ]);
1446}
1447
1448fn import_functions<Pe: ImageNtHeaders, F>(
1449    thunks: &mut ImportThunkList,
1450    dll_name: &[u8],
1451    hint_name: F,
1452    mut rva: u32,
1453    is_32: bool,
1454    data_functions: &mut Vec<DataFunction>,
1455    nb_functions_total: &mut usize,
1456) -> Vec<Value>
1457where
1458    F: Fn(u32) -> object::Result<Vec<u8>>,
1459{
1460    // FIXME: yara does rva adjusments, do we need to do it too?
1461    let mut functions = Vec::new();
1462    while let Ok(Some(thunk)) = thunks.next::<Pe>() {
1463        if *nb_functions_total >= MAX_PE_IMPORTS {
1464            break;
1465        }
1466
1467        if add_thunk::<Pe, _>(
1468            thunk,
1469            dll_name,
1470            rva,
1471            is_32,
1472            &hint_name,
1473            &mut functions,
1474            data_functions,
1475        ) {
1476            *nb_functions_total += 1;
1477        }
1478        rva += if is_32 { 4 } else { 8 };
1479    }
1480    functions
1481}
1482
1483fn add_thunk<Pe: ImageNtHeaders, F>(
1484    thunk: Pe::ImageThunkData,
1485    dll_name: &[u8],
1486    rva: u32,
1487    is_32: bool,
1488    hint_name: F,
1489    functions: &mut Vec<Value>,
1490    data_functions: &mut Vec<DataFunction>,
1491) -> bool
1492where
1493    F: Fn(u32) -> object::Result<Vec<u8>>,
1494{
1495    if thunk.is_ordinal() {
1496        let raw = if is_32 {
1497            thunk.raw() & 0x7FFF_FFFF
1498        } else {
1499            thunk.raw() & 0x7FFF_FFFF_FFFF_FFFF
1500        };
1501
1502        let Ok(ordinal) = u16::try_from(raw) else {
1503            // Corrupted ordinal value, ignore
1504            return false;
1505        };
1506        let name = ord::ord_lookup(dll_name, ordinal);
1507
1508        data_functions.push(DataFunction {
1509            name: name.clone(),
1510            ordinal: Some(ordinal),
1511            rva,
1512        });
1513
1514        functions.push(Value::object([
1515            ("name", name.into()),
1516            ("ordinal", ordinal.into()),
1517            ("rva", rva.into()),
1518        ]));
1519        true
1520    } else {
1521        let Ok(name) = hint_name(thunk.address()) else {
1522            return false;
1523        };
1524
1525        if !is_import_name_valid(&name) {
1526            return false;
1527        }
1528
1529        data_functions.push(DataFunction {
1530            name: name.clone(),
1531            ordinal: None,
1532            rva,
1533        });
1534        functions.push(Value::object([("name", name.into()), ("rva", rva.into())]));
1535        true
1536    }
1537}
1538
1539// This mirrors what pefile does.
1540// See https://github.com/erocarrera/pefile/blob/593d094e35198dad92aaf040bef17eb800c8a373/pefile.py#L2326-L2348
1541fn is_import_name_valid(name: &[u8]) -> bool {
1542    if name.is_empty() {
1543        false
1544    } else {
1545        name.iter().all(|b| {
1546            b.is_ascii_alphanumeric()
1547                || [b'.', b'_', b'?', b'@', b'$', b'(', b')', b'<', b'>'].contains(b)
1548        })
1549    }
1550}
1551
1552fn add_delay_load_imports<Pe: ImageNtHeaders>(
1553    data_dirs: &DataDirectories,
1554    mem: &[u8],
1555    sections: &utils::SectionTable,
1556    data: &mut Data,
1557    out: &mut HashMap<&'static str, Value>,
1558) {
1559    let mut imports = Vec::new();
1560    let mut nb_functions_total = 0;
1561
1562    let table = data_dirs
1563        .get(pe::IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT)
1564        .and_then(|dir| {
1565            let import_va = dir.virtual_address.get(LE);
1566            let (section_data, section_va) = sections.get_section_containing(mem, import_va)?;
1567            Some(DelayLoadImportTable::new(
1568                section_data,
1569                section_va,
1570                import_va,
1571            ))
1572        });
1573    let descriptors = table.as_ref().and_then(|table| table.descriptors().ok());
1574    if let (Some(table), Some(mut descriptors)) = (table, descriptors) {
1575        while let Ok(Some(import_desc)) = descriptors.next() {
1576            let library = match table.name(import_desc.dll_name_rva.get(LE)) {
1577                Ok(name) => name.to_vec(),
1578                Err(_) => continue,
1579            };
1580            if !dll_name_is_valid(&library) {
1581                continue;
1582            }
1583
1584            let mut data_functions = Vec::new();
1585            let functions = table
1586                .thunks(import_desc.import_name_table_rva.get(LE))
1587                .ok()
1588                .map(|mut thunks| {
1589                    import_functions::<Pe, _>(
1590                        &mut thunks,
1591                        &library,
1592                        |hint| table.hint_name(hint).map(|(_, name)| name.to_vec()),
1593                        import_desc.import_address_table_rva.get(LE),
1594                        data.is_32bit,
1595                        &mut data_functions,
1596                        &mut nb_functions_total,
1597                    )
1598                });
1599
1600            data.delayed_imports.push(DataImport {
1601                dll_name: library.clone(),
1602                functions: data_functions,
1603            });
1604
1605            imports.push(Value::object([
1606                ("library_name", library.into()),
1607                (
1608                    "number_of_functions",
1609                    functions.as_ref().map(Vec::len).into(),
1610                ),
1611                (
1612                    "functions",
1613                    functions.map_or(Value::Undefined, Value::Array),
1614                ),
1615            ]));
1616            if imports.len() >= MAX_PE_IMPORTS {
1617                break;
1618            }
1619        }
1620    }
1621
1622    out.extend([
1623        (
1624            "number_of_delayed_imported_functions",
1625            nb_functions_total.into(),
1626        ),
1627        ("number_of_delayed_imports", imports.len().into()),
1628        ("delayed_import_details", Value::Array(imports)),
1629    ]);
1630}
1631
1632fn dll_name_is_valid(dll_name: &[u8]) -> bool {
1633    dll_name.iter().all(|c| {
1634        *c >= b' '
1635            && *c <= 0x7e
1636            && *c != b'\"'
1637            && *c != b'*'
1638            && *c != b'<'
1639            && *c != b'>'
1640            && *c != b'?'
1641            && *c != b'|'
1642    })
1643}
1644
1645fn add_exports(
1646    data_dirs: &DataDirectories,
1647    mem: &[u8],
1648    sections: &utils::SectionTable,
1649    data: &mut Data,
1650    out: &mut HashMap<&'static str, Value>,
1651) {
1652    let export_table = data_dirs
1653        .get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT)
1654        .and_then(|dir| {
1655            let export_va = dir.virtual_address.get(LE);
1656            let export_data = sections.get_dir_data(mem, *dir)?;
1657            ExportTable::parse(export_data, export_va).ok()
1658        });
1659
1660    if let Some(table) = export_table {
1661        let ordinal_base = table.ordinal_base() as usize;
1662        let addresses = table.addresses();
1663        let mut details: Vec<_> = addresses
1664            .iter()
1665            .take(MAX_PE_EXPORTS)
1666            .enumerate()
1667            .map(|(i, address)| {
1668                let address = address.get(LE);
1669                let forward_name = table.forward_string(address).ok().flatten().map(|forward| {
1670                    if forward.len() > MAX_EXPORT_NAME_LENGTH {
1671                        forward[..MAX_EXPORT_NAME_LENGTH].to_vec()
1672                    } else {
1673                        forward.to_vec()
1674                    }
1675                });
1676
1677                data.exports.push(DataExport {
1678                    name: None,
1679                    ordinal: ordinal_base + i,
1680                });
1681
1682                Value::object([
1683                    ("ordinal", (ordinal_base + i).into()),
1684                    (
1685                        "offset",
1686                        match forward_name {
1687                            Some(_) => Value::Undefined,
1688                            // -1 is set by libyara to indicate an invalid offset.
1689                            None => match utils::va_to_file_offset(mem, sections, address) {
1690                                Some(v) => v.into(),
1691                                None => Value::Undefined,
1692                            },
1693                        },
1694                    ),
1695                    ("forward_name", forward_name.into()),
1696                    ("rva", address.into()),
1697                ])
1698            })
1699            .collect();
1700
1701        // Now, add names
1702        for (name_pointer, ordinal_index) in table.name_iter() {
1703            let Ok(mut name) = table.name_from_pointer(name_pointer) else {
1704                continue;
1705            };
1706            if name.len() > MAX_EXPORT_NAME_LENGTH {
1707                name = &name[..MAX_EXPORT_NAME_LENGTH];
1708            }
1709            let ordinal_index = usize::from(ordinal_index);
1710
1711            if let Some(Value::Object(map)) = details.get_mut(ordinal_index) {
1712                let _r = map.insert("name", Value::bytes(name));
1713            }
1714            if let Some(export) = data.exports.get_mut(ordinal_index) {
1715                export.name = Some(name.to_vec());
1716            }
1717        }
1718
1719        let dir = table.directory();
1720        out.extend([
1721            ("export_timestamp", dir.time_date_stamp.get(LE).into()),
1722            (
1723                "dll_name",
1724                table
1725                    .name_from_pointer(dir.name.get(LE))
1726                    .ok()
1727                    .map(<[u8]>::to_vec)
1728                    .into(),
1729            ),
1730            ("number_of_exports", details.len().into()),
1731            ("export_details", Value::Array(details)),
1732        ]);
1733    } else {
1734        let _r = out.insert("number_of_exports", Value::Integer(0));
1735    }
1736}
1737
1738fn data_directories(dirs: DataDirectories) -> Value {
1739    Value::Array(
1740        dirs.iter()
1741            .take(MAX_NB_DATA_DIRECTORIES)
1742            .map(|dir| {
1743                Value::object([
1744                    ("virtual_address", dir.virtual_address.get(LE).into()),
1745                    ("size", dir.size.get(LE).into()),
1746                ])
1747            })
1748            .collect(),
1749    )
1750}
1751
1752fn sections_to_value(
1753    sections: &utils::SectionTable,
1754    strings: Option<StringTable>,
1755    data: &mut Data,
1756) -> Value {
1757    Value::Array(
1758        sections
1759            .iter()
1760            .take(MAX_PE_SECTIONS)
1761            .map(|section| {
1762                let mut name = section.name.as_slice();
1763                if let Some(last_non_zero_pos) = name
1764                    .iter()
1765                    .enumerate()
1766                    .rev()
1767                    .find_map(|(i, v)| (*v != 0).then_some(i))
1768                {
1769                    name = &name[..=last_non_zero_pos];
1770                }
1771                let full_name = match (strings, section.name_offset()) {
1772                    // Get full name from the strings table
1773                    // TODO: yara rejects a full name that contains non isprint bytes. But why?
1774                    (Some(strings), Ok(Some(offset))) => strings.get(offset).ok(),
1775                    // No offset into string table, full name is the same as the name
1776                    (_, Ok(None)) => Some(name),
1777                    (_, _) => None,
1778                };
1779                let raw_data_offset = i64::from(section.pointer_to_raw_data.get(LE));
1780                let raw_data_size = i64::from(section.size_of_raw_data.get(LE));
1781                let virtual_address = i64::from(section.virtual_address.get(LE));
1782                let virtual_size = i64::from(section.virtual_size.get(LE));
1783
1784                data.sections.push(DataSection {
1785                    name: name.to_vec(),
1786                    raw_data_offset,
1787                    raw_data_size,
1788                    virtual_address,
1789                    virtual_size,
1790                });
1791
1792                Value::object([
1793                    ("name", name.to_vec().into()),
1794                    ("full_name", full_name.map(<[u8]>::to_vec).into()),
1795                    ("characteristics", section.characteristics.get(LE).into()),
1796                    ("virtual_address", virtual_address.into()),
1797                    ("virtual_size", virtual_size.into()),
1798                    ("raw_data_size", raw_data_size.into()),
1799                    ("raw_data_offset", raw_data_offset.into()),
1800                    (
1801                        "pointer_to_relocations",
1802                        section.pointer_to_relocations.get(LE).into(),
1803                    ),
1804                    (
1805                        "pointer_to_line_numbers",
1806                        section.pointer_to_linenumbers.get(LE).into(),
1807                    ),
1808                    (
1809                        "number_of_relocations",
1810                        section.number_of_relocations.get(LE).into(),
1811                    ),
1812                    (
1813                        "number_of_line_numbers",
1814                        section.number_of_linenumbers.get(LE).into(),
1815                    ),
1816                ])
1817            })
1818            .collect(),
1819    )
1820}
1821
1822fn overlay(sections: &utils::SectionTable, mem: &[u8]) -> Value {
1823    let offset = sections.max_section_file_offset();
1824
1825    if offset < mem.len() as u64 {
1826        Value::object([
1827            ("offset", offset.into()),
1828            ("size", (mem.len() as u64 - offset).into()),
1829        ])
1830    } else {
1831        Value::object([("offset", 0.into()), ("size", 0.into())])
1832    }
1833}
1834
1835fn add_resources(
1836    data_dirs: &DataDirectories,
1837    mem: &[u8],
1838    sections: &utils::SectionTable,
1839    data: &mut Data,
1840    out: &mut HashMap<&'static str, Value>,
1841) {
1842    let mut infos = Vec::new();
1843
1844    let resource_dir = data_dirs
1845        .get(pe::IMAGE_DIRECTORY_ENTRY_RESOURCE)
1846        .and_then(|dir| {
1847            let rsrc_data = sections.get_dir_data(mem, *dir)?;
1848            Some(ResourceDirectory::new(rsrc_data))
1849        });
1850
1851    let root = resource_dir.as_ref().and_then(|dir| dir.root().ok());
1852    if let (Some(dir), Some(root)) = (resource_dir, root) {
1853        let mut resources = Vec::new();
1854
1855        'outer: for entry in root.entries {
1856            // Copied from 1242223b04f2 in libyara
1857            if entry.offset_to_data_or_directory.get(LE) == 0 {
1858                continue;
1859            }
1860
1861            // First level is type
1862            let ty = entry.name_or_id.get(LE);
1863            let ty_name = match entry.name_or_id() {
1864                ResourceNameOrId::Name(name) => name.raw_data(dir).ok(),
1865                ResourceNameOrId::Id(_) => None,
1866            };
1867
1868            let Ok(ResourceDirectoryEntryData::Table(table)) = entry.data(dir) else {
1869                continue;
1870            };
1871            for entry in table.entries {
1872                // Second level is id
1873                let id = entry.name_or_id.get(LE);
1874                let id_name = resource_entry_name(*entry, dir);
1875
1876                let Ok(ResourceDirectoryEntryData::Table(table)) = entry.data(dir) else {
1877                    continue;
1878                };
1879                for entry in table.entries {
1880                    // Third level is language
1881                    let lang = entry.name_or_id.get(LE);
1882                    let lang_name = match entry.name_or_id() {
1883                        ResourceNameOrId::Name(name) => name.raw_data(dir).ok(),
1884                        ResourceNameOrId::Id(_) => None,
1885                    };
1886
1887                    if let Ok(ResourceDirectoryEntryData::Data(entry_data)) = entry.data(dir) {
1888                        // Copied from 620963092c4 and 44fd0945446665 in libyara
1889                        // The goal is to reject corrupted/random values while accepting
1890                        // truncated files (where the size/offset may get out of bound compared
1891                        // to the scanned memory.
1892                        let size = entry_data.size.get(LE);
1893                        if size == 0 || size > 0x3FFF_FFFF {
1894                            continue;
1895                        }
1896
1897                        let rva = entry_data.offset_to_data.get(LE);
1898                        let offset = utils::va_to_file_offset(mem, sections, rva);
1899                        if ty == u32::from(pe::RT_VERSION) {
1900                            if let Some(offset) = offset {
1901                                version_info::read_version_info(mem, offset as usize, &mut infos);
1902                            }
1903                        }
1904
1905                        data.resource_languages.push(lang);
1906
1907                        resources.push(Value::object([
1908                            ("rva", rva.into()),
1909                            ("length", entry_data.size.get(LE).into()),
1910                            ("offset", offset.into()),
1911                            ("type_string", ty_name.map(<[u8]>::to_vec).into()),
1912                            (
1913                                "type",
1914                                match ty_name {
1915                                    Some(_) => Value::Undefined,
1916                                    None => ty.into(),
1917                                },
1918                            ),
1919                            ("name_string", id_name.map(<[u8]>::to_vec).into()),
1920                            (
1921                                "id",
1922                                match id_name {
1923                                    Some(_) => Value::Undefined,
1924                                    None => id.into(),
1925                                },
1926                            ),
1927                            ("language_string", lang_name.map(<[u8]>::to_vec).into()),
1928                            (
1929                                "language",
1930                                match lang_name {
1931                                    Some(_) => Value::Undefined,
1932                                    None => lang.into(),
1933                                },
1934                            ),
1935                        ]));
1936                        if resources.len() >= MAX_RESOURCES {
1937                            break 'outer;
1938                        }
1939                    }
1940                }
1941            }
1942        }
1943
1944        out.extend([
1945            ("number_of_resources", resources.len().into()),
1946            (
1947                "resource_timestamp",
1948                root.header.time_date_stamp.get(LE).into(),
1949            ),
1950            (
1951                "resource_version",
1952                Value::object([
1953                    ("major", root.header.major_version.get(LE).into()),
1954                    ("minor", root.header.minor_version.get(LE).into()),
1955                ]),
1956            ),
1957            ("resources", Value::Array(resources)),
1958        ]);
1959    } else {
1960        let _r = out.insert("number_of_resources", Value::Integer(0));
1961    }
1962
1963    out.extend([
1964        ("number_of_version_infos", infos.len().into()),
1965        (
1966            "version_info",
1967            Value::Dictionary(
1968                infos
1969                    .iter()
1970                    .map(|info| (info.key.clone(), info.value.clone().into()))
1971                    .collect(),
1972            ),
1973        ),
1974        (
1975            "version_info_list",
1976            Value::Array(
1977                infos
1978                    .into_iter()
1979                    .map(|info| {
1980                        Value::object([("key", info.key.into()), ("value", info.value.into())])
1981                    })
1982                    .collect(),
1983            ),
1984        ),
1985    ]);
1986}
1987
1988fn resource_entry_name(
1989    entry: pe::ImageResourceDirectoryEntry,
1990    dir: ResourceDirectory,
1991) -> Option<&[u8]> {
1992    match entry.name_or_id() {
1993        ResourceNameOrId::Name(resource_name) => match resource_name.raw_data(dir) {
1994            Ok(name) if name.len() <= 1000 => Some(name),
1995            _ => None,
1996        },
1997        ResourceNameOrId::Id(_) => None,
1998    }
1999}
2000
2001fn bool_to_int_value(b: bool) -> Value {
2002    Value::Integer(b.into())
2003}
2004
2005impl Pe {
2006    fn calculate_checksum(ctx: &mut EvalContext, _: Vec<Value>) -> Option<Value> {
2007        let mem = ctx.mem.get_direct()?;
2008
2009        // Compute offset of checksum in the file: this is replaced by 0 when computing the
2010        // checksum
2011        let dos_header = ImageDosHeader::parse(mem).ok()?;
2012        // 64 is the offset of the checksum in the optional header, and 24 is the offset of the
2013        // optional header in the nt headers: See
2014        // <https://docs.microsoft.com/en-us/windows/win32/debug/pe-format>
2015        let csum_offset = dos_header.nt_headers_offset() + 64 + 24;
2016
2017        // Add data as LE u32 with overflow
2018        let mut csum: u64 = 0;
2019        let mut idx = 0;
2020        let mut cursor = mem;
2021        while cursor.len() >= 4 {
2022            if idx != csum_offset {
2023                let dword = u32::from_le_bytes([cursor[0], cursor[1], cursor[2], cursor[3]]);
2024
2025                csum += u64::from(dword);
2026                if csum > 0xFFFF_FFFF {
2027                    csum = (csum & 0xFFFF_FFFF) + (csum >> 32);
2028                }
2029            }
2030
2031            cursor = &cursor[4..];
2032            idx += 4;
2033        }
2034
2035        // pad with 0 for the last chunk
2036        let dword = match cursor {
2037            [a] => u32::from_le_bytes([*a, 0, 0, 0]),
2038            [a, b] => u32::from_le_bytes([*a, *b, 0, 0]),
2039            [a, b, c] => u32::from_le_bytes([*a, *b, *c, 0]),
2040            _ => 0,
2041        };
2042        csum += u64::from(dword);
2043        if csum > 0xFFFF_FFFF {
2044            csum = (csum & 0xFFFF_FFFF) + (csum >> 32);
2045        }
2046
2047        // Fold the checksum to a u16
2048        let mut csum = (csum & 0xFFFF) + (csum >> 16);
2049        csum += csum >> 16;
2050        csum &= 0xFFFF;
2051
2052        // Finally, add the filesize
2053        #[allow(clippy::cast_possible_truncation)]
2054        Some((csum as usize).wrapping_add(mem.len()).into())
2055    }
2056
2057    fn section_index(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2058        let mut args = args.into_iter();
2059        let arg = args.next()?;
2060
2061        let data = ctx.module_data.get::<Self>()?;
2062
2063        match arg {
2064            Value::Bytes(section_name) => data
2065                .sections
2066                .iter()
2067                .position(|sec| sec.name == section_name)
2068                .map(Into::into),
2069            Value::Integer(addr) => {
2070                let index = if ctx.process_memory {
2071                    data.sections.iter().position(|sec| {
2072                        addr >= sec.virtual_address && addr - sec.virtual_address < sec.virtual_size
2073                    })?
2074                } else {
2075                    data.sections.iter().position(|sec| {
2076                        addr >= sec.raw_data_offset
2077                            && addr - sec.raw_data_offset < sec.raw_data_size
2078                    })?
2079                };
2080
2081                Some(index.into())
2082            }
2083            _ => None,
2084        }
2085    }
2086
2087    fn exports(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2088        let mut args = args.into_iter();
2089        let arg = args.next()?;
2090
2091        let data = ctx.module_data.get::<Self>()?;
2092
2093        let res = match arg {
2094            Value::Bytes(function_name) => data.exports.iter().any(|export| {
2095                export
2096                    .name
2097                    .as_ref()
2098                    .is_some_and(|name| name.eq_ignore_ascii_case(&function_name))
2099            }),
2100            Value::Integer(ordinal) => match usize::try_from(ordinal) {
2101                Ok(ord) => data.exports.iter().any(|export| export.ordinal == ord),
2102                Err(_) => false,
2103            },
2104            Value::Regex(function_name_regex) => data.exports.iter().any(|export| {
2105                export
2106                    .name
2107                    .as_ref()
2108                    .is_some_and(|name| function_name_regex.is_match(name))
2109            }),
2110            _ => return None,
2111        };
2112
2113        Some(bool_to_int_value(res))
2114    }
2115
2116    fn exports_index(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2117        let mut args = args.into_iter();
2118        let arg = args.next()?;
2119
2120        let data = ctx.module_data.get::<Self>()?;
2121        // XXX: yara does this, for some reason
2122        if data.exports.is_empty() {
2123            return None;
2124        }
2125
2126        let res = match arg {
2127            Value::Bytes(function_name) => data.exports.iter().position(|export| {
2128                export
2129                    .name
2130                    .as_ref()
2131                    .is_some_and(|name| name.eq_ignore_ascii_case(&function_name))
2132            })?,
2133            Value::Integer(ordinal) => {
2134                let ordinal = usize::try_from(ordinal).ok()?;
2135                if ordinal == 0 || ordinal > data.exports.len() {
2136                    return None;
2137                }
2138
2139                data.exports
2140                    .iter()
2141                    .position(|export| export.ordinal == ordinal)?
2142            }
2143            Value::Regex(function_name_regex) => data.exports.iter().position(|export| {
2144                export
2145                    .name
2146                    .as_ref()
2147                    .is_some_and(|name| function_name_regex.is_match(name))
2148            })?,
2149            _ => return None,
2150        };
2151
2152        Some(res.into())
2153    }
2154
2155    fn imports(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2156        let mut args = args.into_iter();
2157        let first = args.next()?;
2158        let second = args.next();
2159        let third = args.next();
2160
2161        let data = ctx.module_data.get::<Self>()?;
2162
2163        match (first, second, third) {
2164            (Value::Bytes(dll_name), Some(Value::Bytes(function_name)), None) => {
2165                Some(bool_to_int_value(
2166                    data.find_function(&dll_name, &function_name, false)
2167                        .is_some(),
2168                ))
2169            }
2170            (Value::Bytes(dll_name), Some(Value::Integer(ordinal)), None) => {
2171                Some(bool_to_int_value(
2172                    data.find_function_ordinal(&dll_name, ordinal, false)
2173                        .is_some(),
2174                ))
2175            }
2176            (Value::Bytes(dll_name), None, None) => {
2177                Some(data.nb_functions(&dll_name, false).into())
2178            }
2179            (Value::Regex(dll_name), Some(Value::Regex(function_name)), None) => Some(
2180                data.nb_functions_regex(&dll_name, &function_name, false)
2181                    .into(),
2182            ),
2183            (
2184                Value::Integer(flags),
2185                Some(Value::Bytes(dll_name)),
2186                Some(Value::Bytes(function_name)),
2187            ) => {
2188                if flags & (ImportType::Standard as i64) != 0
2189                    && data
2190                        .find_function(&dll_name, &function_name, false)
2191                        .is_some()
2192                {
2193                    return Some(Value::Integer(1));
2194                }
2195                if flags & (ImportType::Delayed as i64) != 0
2196                    && data
2197                        .find_function(&dll_name, &function_name, true)
2198                        .is_some()
2199                {
2200                    return Some(Value::Integer(1));
2201                }
2202
2203                Some(Value::Integer(0))
2204            }
2205            (
2206                Value::Integer(flags),
2207                Some(Value::Bytes(dll_name)),
2208                Some(Value::Integer(ordinal)),
2209            ) => {
2210                if flags & (ImportType::Standard as i64) != 0
2211                    && data
2212                        .find_function_ordinal(&dll_name, ordinal, false)
2213                        .is_some()
2214                {
2215                    return Some(Value::Integer(1));
2216                }
2217                if flags & (ImportType::Delayed as i64) != 0
2218                    && data
2219                        .find_function_ordinal(&dll_name, ordinal, true)
2220                        .is_some()
2221                {
2222                    return Some(Value::Integer(1));
2223                }
2224
2225                Some(Value::Integer(0))
2226            }
2227            (Value::Integer(flags), Some(Value::Bytes(dll_name)), None) => {
2228                let mut res = 0;
2229                if flags & (ImportType::Standard as i64) != 0 {
2230                    res += data.nb_functions(&dll_name, false);
2231                }
2232                if flags & (ImportType::Delayed as i64) != 0 {
2233                    res += data.nb_functions(&dll_name, true);
2234                }
2235                Some(res.into())
2236            }
2237            (
2238                Value::Integer(flags),
2239                Some(Value::Regex(dll_name)),
2240                Some(Value::Regex(function_name)),
2241            ) => {
2242                let mut res = 0;
2243                if flags & (ImportType::Standard as i64) != 0 {
2244                    res += data.nb_functions_regex(&dll_name, &function_name, false);
2245                }
2246                if flags & (ImportType::Delayed as i64) != 0 {
2247                    res += data.nb_functions_regex(&dll_name, &function_name, true);
2248                }
2249                Some(res.into())
2250            }
2251            _ => None,
2252        }
2253    }
2254
2255    fn import_rva(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2256        let mut args = args.into_iter();
2257        let first = args.next()?;
2258        let second = args.next()?;
2259
2260        let data = ctx.module_data.get::<Self>()?;
2261
2262        match (first, second) {
2263            (Value::Bytes(dll_name), Value::Bytes(function_name)) => data
2264                .find_function(&dll_name, &function_name, false)
2265                .map(|v| v.rva.into()),
2266            (Value::Bytes(dll_name), Value::Integer(ordinal)) => data
2267                .find_function_ordinal(&dll_name, ordinal, false)
2268                .map(|v| v.rva.into()),
2269            _ => None,
2270        }
2271    }
2272
2273    fn delayed_import_rva(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2274        let mut args = args.into_iter();
2275        let first = args.next()?;
2276        let second = args.next()?;
2277
2278        let data = ctx.module_data.get::<Self>()?;
2279
2280        match (first, second) {
2281            (Value::Bytes(dll_name), Value::Bytes(function_name)) => data
2282                .find_function(&dll_name, &function_name, true)
2283                .map(|v| v.rva.into()),
2284            (Value::Bytes(dll_name), Value::Integer(ordinal)) => data
2285                .find_function_ordinal(&dll_name, ordinal, true)
2286                .map(|v| v.rva.into()),
2287            _ => None,
2288        }
2289    }
2290
2291    fn locale(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2292        let mut args = args.into_iter();
2293        let locale: i64 = args.next()?.try_into().ok()?;
2294
2295        let data = ctx.module_data.get::<Self>()?;
2296        Some(bool_to_int_value(
2297            data.resource_languages
2298                .iter()
2299                .any(|language| i64::from(language & 0xFFFF) == locale),
2300        ))
2301    }
2302
2303    fn language(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2304        let mut args = args.into_iter();
2305        let lang: i64 = args.next()?.try_into().ok()?;
2306
2307        let data = ctx.module_data.get::<Self>()?;
2308        Some(bool_to_int_value(
2309            data.resource_languages
2310                .iter()
2311                .any(|language| i64::from(language & 0xFF) == lang),
2312        ))
2313    }
2314
2315    fn is_dll(ctx: &mut EvalContext, _: Vec<Value>) -> Option<Value> {
2316        let data = ctx.module_data.get::<Self>()?;
2317        Some(data.is_dll.into())
2318    }
2319
2320    fn is_32bit(ctx: &mut EvalContext, _: Vec<Value>) -> Option<Value> {
2321        let data = ctx.module_data.get::<Self>()?;
2322        Some(bool_to_int_value(data.is_32bit))
2323    }
2324
2325    fn is_64bit(ctx: &mut EvalContext, _: Vec<Value>) -> Option<Value> {
2326        let data = ctx.module_data.get::<Self>()?;
2327        Some(bool_to_int_value(!data.is_32bit))
2328    }
2329
2330    fn rich_signature_version(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2331        let mut args = args.into_iter();
2332        let first = args.next()?;
2333        let second = args.next();
2334
2335        let data = ctx.module_data.get::<Self>()?;
2336
2337        let res = match (first, second) {
2338            (Value::Integer(version), Some(Value::Integer(toolid))) => {
2339                data.count_rich_entries(Some(version), Some(toolid))
2340            }
2341            (Value::Integer(version), None) => data.count_rich_entries(Some(version), None),
2342            _ => return None,
2343        };
2344
2345        Some(res.into())
2346    }
2347
2348    fn rich_signature_toolid(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2349        let mut args = args.into_iter();
2350        let first = args.next()?;
2351        let second = args.next();
2352
2353        let data = ctx.module_data.get::<Self>()?;
2354
2355        let res = match (first, second) {
2356            (Value::Integer(toolid), Some(Value::Integer(version))) => {
2357                data.count_rich_entries(Some(version), Some(toolid))
2358            }
2359            (Value::Integer(toolid), None) => data.count_rich_entries(None, Some(toolid)),
2360            _ => return None,
2361        };
2362
2363        Some(res.into())
2364    }
2365
2366    #[cfg(feature = "hash")]
2367    fn imphash(ctx: &mut EvalContext, _: Vec<Value>) -> Option<Value> {
2368        use md5::{Digest, Md5};
2369
2370        let data = ctx.module_data.get::<Self>()?;
2371
2372        let mut hasher = Md5::new();
2373        let mut first = true;
2374        for dll in &data.imports {
2375            let mut dll_name = dll.dll_name.to_ascii_lowercase();
2376            if dll_name.ends_with(b".ocx")
2377                || dll_name.ends_with(b".sys")
2378                || dll_name.ends_with(b".dll")
2379            {
2380                dll_name.truncate(dll_name.len() - 4);
2381            }
2382
2383            for fun in &dll.functions {
2384                let fun_name = fun.name.to_ascii_lowercase();
2385
2386                if !first {
2387                    hasher.update([b',']);
2388                }
2389                hasher.update(&dll_name);
2390                hasher.update([b'.']);
2391                hasher.update(fun_name);
2392                first = false;
2393            }
2394        }
2395
2396        Some(Value::Bytes(super::hex_encode(hasher.finalize())))
2397    }
2398
2399    fn rva_to_offset(ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
2400        let rva: i64 = args.into_iter().next()?.try_into().ok()?;
2401        let rva: u32 = rva.try_into().ok()?;
2402
2403        // TODO: handle fragmented memory for this
2404        let mem = ctx.mem.get_direct()?;
2405
2406        // We cannot save the SectionTable in the data, because it is a no-copy struct borrowing on
2407        // the scanned mem. Instead, we will reparse the mem and rebuild the SectionTable.
2408        // This isn't that costly, and this function shouldn't be used that much anyway.
2409        let dos_header = ImageDosHeader::parse(mem).ok()?;
2410        let mut offset = dos_header.nt_headers_offset().into();
2411        let section_table = match FileKind::parse(mem) {
2412            Ok(FileKind::Pe32) => {
2413                let (nt_headers, _) = ImageNtHeaders32::parse(mem, &mut offset).ok()?;
2414                utils::SectionTable::new(nt_headers, mem, offset)?
2415            }
2416            Ok(FileKind::Pe64) => {
2417                let (nt_headers, _) = ImageNtHeaders64::parse(mem, &mut offset).ok()?;
2418                utils::SectionTable::new(nt_headers, mem, offset)?
2419            }
2420            _ => return None,
2421        };
2422
2423        utils::va_to_file_offset(mem, &section_table, rva).map(Into::into)
2424    }
2425}
2426
2427#[derive(Default)]
2428pub struct Data {
2429    imports: Vec<DataImport>,
2430    delayed_imports: Vec<DataImport>,
2431    exports: Vec<DataExport>,
2432    sections: Vec<DataSection>,
2433    rich_entries: Vec<DataRichEntry>,
2434    resource_languages: Vec<u32>,
2435    is_32bit: bool,
2436    is_dll: u16,
2437    found_pe: bool,
2438}
2439
2440struct DataImport {
2441    dll_name: Vec<u8>,
2442    functions: Vec<DataFunction>,
2443}
2444
2445struct DataExport {
2446    name: Option<Vec<u8>>,
2447    ordinal: usize,
2448}
2449
2450struct DataFunction {
2451    name: Vec<u8>,
2452    ordinal: Option<u16>,
2453    rva: u32,
2454}
2455
2456struct DataSection {
2457    name: Vec<u8>,
2458    raw_data_offset: i64,
2459    raw_data_size: i64,
2460    virtual_address: i64,
2461    virtual_size: i64,
2462}
2463
2464struct DataRichEntry {
2465    version: u16,
2466    toolid: u16,
2467    times: u32,
2468}
2469
2470impl Data {
2471    fn get_imports(&self, delayed: bool) -> &[DataImport] {
2472        if delayed {
2473            &self.delayed_imports
2474        } else {
2475            &self.imports
2476        }
2477    }
2478
2479    fn find_function(
2480        &self,
2481        dll_name: &[u8],
2482        fun_name: &[u8],
2483        delayed: bool,
2484    ) -> Option<&DataFunction> {
2485        self.get_imports(delayed)
2486            .iter()
2487            .find(|imp| imp.dll_name.eq_ignore_ascii_case(dll_name))
2488            .and_then(|imp| {
2489                imp.functions
2490                    .iter()
2491                    .find(|f| f.name.eq_ignore_ascii_case(fun_name))
2492            })
2493    }
2494
2495    fn find_function_ordinal(
2496        &self,
2497        dll_name: &[u8],
2498        ordinal: i64,
2499        delayed: bool,
2500    ) -> Option<&DataFunction> {
2501        self.get_imports(delayed)
2502            .iter()
2503            .find(|imp| imp.dll_name.eq_ignore_ascii_case(dll_name))
2504            .and_then(|imp| {
2505                imp.functions.iter().find(|f| match f.ordinal {
2506                    Some(v) => i64::from(v) == ordinal,
2507                    None => false,
2508                })
2509            })
2510    }
2511
2512    fn nb_functions(&self, dll_name: &[u8], delayed: bool) -> usize {
2513        self.get_imports(delayed)
2514            .iter()
2515            .find(|imp| imp.dll_name.eq_ignore_ascii_case(dll_name))
2516            .map_or(0, |imp| imp.functions.len())
2517    }
2518
2519    fn nb_functions_regex(&self, dll_regex: &Regex, fun_regex: &Regex, delayed: bool) -> u32 {
2520        let mut nb_matches = 0;
2521
2522        for imp in self.get_imports(delayed) {
2523            if !dll_regex.is_match(&imp.dll_name) {
2524                continue;
2525            }
2526            for fun in &imp.functions {
2527                if fun_regex.is_match(&fun.name) {
2528                    nb_matches += 1;
2529                }
2530            }
2531        }
2532        nb_matches
2533    }
2534
2535    fn count_rich_entries(&self, version: Option<i64>, toolid: Option<i64>) -> u64 {
2536        let version = match version {
2537            Some(v) => match u16::try_from(v) {
2538                Ok(v) => Some(v),
2539                Err(_) => return 0,
2540            },
2541            None => None,
2542        };
2543        let toolid = match toolid {
2544            Some(v) => match u16::try_from(v) {
2545                Ok(v) => Some(v),
2546                Err(_) => return 0,
2547            },
2548            None => None,
2549        };
2550
2551        self.rich_entries
2552            .iter()
2553            .map(|entry| {
2554                let mut matched = true;
2555                if let Some(v) = version {
2556                    matched = matched && v == entry.version;
2557                }
2558                if let Some(t) = toolid {
2559                    matched = matched && t == entry.toolid;
2560                }
2561
2562                if matched {
2563                    u64::from(entry.times)
2564                } else {
2565                    0
2566                }
2567            })
2568            .sum()
2569    }
2570}