manageiq-smartstate 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +4 -0
- data/.rspec_ci +4 -0
- data/.travis.yml +15 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +202 -0
- data/README.md +45 -0
- data/Rakefile +23 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/MiqContainerGroup/MiqContainerGroup.rb +31 -0
- data/lib/MiqVm/MiqLocalVm.rb +50 -0
- data/lib/MiqVm/MiqRhevmVm.rb +179 -0
- data/lib/MiqVm/MiqVm.rb +355 -0
- data/lib/MiqVm/miq_azure_vm.rb +96 -0
- data/lib/MiqVm/miq_scvmm_vm.rb +38 -0
- data/lib/MiqVm/test/camcorder_fleece_test.rb +60 -0
- data/lib/MiqVm/test/localVm.rb +45 -0
- data/lib/MiqVm/test/partitionAlignmentCheck.rb +76 -0
- data/lib/MiqVm/test/remoteVm.rb +65 -0
- data/lib/MiqVm/test/rhevmNfsTest.rb +62 -0
- data/lib/MiqVm/test/rhevmNfsTest2.rb +66 -0
- data/lib/MiqVm/test/rhevmTest.rb +70 -0
- data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackCommon.rb +107 -0
- data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackImage.rb +67 -0
- data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackInstance.rb +182 -0
- data/lib/Scvmm/miq_hyperv_disk.rb +273 -0
- data/lib/Scvmm/miq_scvmm_parse_powershell.rb +75 -0
- data/lib/Scvmm/miq_scvmm_vm_ssa_info.rb +135 -0
- data/lib/Scvmm/test/miq_hyperv_disk_test.rb +33 -0
- data/lib/Scvmm/test/miq_scvmm_vm_ssa_info_test.rb +41 -0
- data/lib/VmLocalDiskAccess/test/localCfg.rb +97 -0
- data/lib/VolumeManager/LVM/logical_volume.rb +75 -0
- data/lib/VolumeManager/LVM/lv_segment.rb +43 -0
- data/lib/VolumeManager/LVM/lvm2disk.rb +158 -0
- data/lib/VolumeManager/LVM/parser.rb +138 -0
- data/lib/VolumeManager/LVM/physical_volume.rb +19 -0
- data/lib/VolumeManager/LVM/scanner.rb +156 -0
- data/lib/VolumeManager/LVM/thin/btree.rb +83 -0
- data/lib/VolumeManager/LVM/thin/constants.rb +86 -0
- data/lib/VolumeManager/LVM/thin/data_map.rb +44 -0
- data/lib/VolumeManager/LVM/thin/mapping_tree.rb +19 -0
- data/lib/VolumeManager/LVM/thin/space_maps.rb +58 -0
- data/lib/VolumeManager/LVM/thin/superblock.rb +136 -0
- data/lib/VolumeManager/LVM/thin.rb +6 -0
- data/lib/VolumeManager/LVM/volume_group.rb +97 -0
- data/lib/VolumeManager/LVM.rb +8 -0
- data/lib/VolumeManager/MiqLdm.rb +546 -0
- data/lib/VolumeManager/MiqLvm.rb +17 -0
- data/lib/VolumeManager/MiqNativeVolumeManager.rb +150 -0
- data/lib/VolumeManager/MiqVolumeManager.rb +277 -0
- data/lib/VolumeManager/VolMgrPlatformSupport.rb +18 -0
- data/lib/VolumeManager/VolMgrPlatformSupportLinux.rb +77 -0
- data/lib/VolumeManager/VolMgrPlatformSupportWin.rb +17 -0
- data/lib/VolumeManager/test/blockDevTest.rb +40 -0
- data/lib/VolumeManager/test/ldm.rb +97 -0
- data/lib/blackbox/VmBlackBox.rb +103 -0
- data/lib/blackbox/xmlStorage.rb +180 -0
- data/lib/db/MiqBdb/MiqBdb.rb +309 -0
- data/lib/db/MiqBdb/MiqBdbBtree.rb +219 -0
- data/lib/db/MiqBdb/MiqBdbHash.rb +199 -0
- data/lib/db/MiqBdb/MiqBdbPage.rb +159 -0
- data/lib/db/MiqBdb/MiqBdbUtil.rb +18 -0
- data/lib/db/MiqSqlite/MiqSqlite3.rb +330 -0
- data/lib/db/MiqSqlite/MiqSqlite3Cell.rb +167 -0
- data/lib/db/MiqSqlite/MiqSqlite3Page.rb +151 -0
- data/lib/db/MiqSqlite/MiqSqlite3Table.rb +124 -0
- data/lib/db/MiqSqlite/MiqSqlite3Util.rb +32 -0
- data/lib/disk/DiskProbe.rb +68 -0
- data/lib/disk/MiqDisk.rb +317 -0
- data/lib/disk/camcorder_test.rb +90 -0
- data/lib/disk/dos_mbr.img +0 -0
- data/lib/disk/modules/AzureBlobDisk.rb +101 -0
- data/lib/disk/modules/LocalDevMod.rb +47 -0
- data/lib/disk/modules/LocalDevProbe.rb +6 -0
- data/lib/disk/modules/MSCommon.rb +352 -0
- data/lib/disk/modules/MSVSDiffDisk.rb +91 -0
- data/lib/disk/modules/MSVSDiskProbe.rb +61 -0
- data/lib/disk/modules/MSVSDynamicDisk.rb +42 -0
- data/lib/disk/modules/MSVSFixedDisk.rb +45 -0
- data/lib/disk/modules/MiqLargeFile.rb +63 -0
- data/lib/disk/modules/MiqLargeFileWin32.rb +107 -0
- data/lib/disk/modules/QcowDisk.rb +692 -0
- data/lib/disk/modules/QcowDiskProbe.rb +34 -0
- data/lib/disk/modules/RawBlockIO.rb +116 -0
- data/lib/disk/modules/RawDisk.rb +45 -0
- data/lib/disk/modules/RawDiskProbe.rb +7 -0
- data/lib/disk/modules/RhevmDescriptor.rb +167 -0
- data/lib/disk/modules/RhevmDiskProbe.rb +52 -0
- data/lib/disk/modules/VMWareCowdDisk.rb +207 -0
- data/lib/disk/modules/VMWareDescriptor.rb +214 -0
- data/lib/disk/modules/VMWareDiskProbe.rb +74 -0
- data/lib/disk/modules/VMWareSparseDisk.rb +189 -0
- data/lib/disk/modules/VhdxDisk.rb +625 -0
- data/lib/disk/modules/VhdxDiskProbe.rb +46 -0
- data/lib/disk/modules/VixDiskMod.rb +54 -0
- data/lib/disk/modules/VixDiskProbe.rb +6 -0
- data/lib/disk/modules/miq_disk_cache.rb +135 -0
- data/lib/disk/modules/miq_dummy_disk.rb +41 -0
- data/lib/disk/modules/vhdx_bat_entry.rb +10 -0
- data/lib/disk/test.rb +66 -0
- data/lib/fs/MetakitFS/MetakitFS.rb +530 -0
- data/lib/fs/MetakitFS/test/Makefile +14 -0
- data/lib/fs/MetakitFS/test/MkCollectFiles.rb +165 -0
- data/lib/fs/MetakitFS/test/MkSelectFiles.rb +30 -0
- data/lib/fs/MetakitFS/test/collect_files.yaml +70 -0
- data/lib/fs/MetakitFS/test/init.rb +3 -0
- data/lib/fs/MetakitFS/test/mk2vmdk.rb +64 -0
- data/lib/fs/MetakitFS/test/mk4test.c +92 -0
- data/lib/fs/MetakitFS/test/mkFsTest.rb +113 -0
- data/lib/fs/MetakitFS/test/proto.rb +97 -0
- data/lib/fs/MiqFS/FsProbe.rb +39 -0
- data/lib/fs/MiqFS/MiqFS.rb +515 -0
- data/lib/fs/MiqFS/modules/AUFSProbe.rb +26 -0
- data/lib/fs/MiqFS/modules/Ext3.rb +305 -0
- data/lib/fs/MiqFS/modules/Ext3Probe.rb +25 -0
- data/lib/fs/MiqFS/modules/Ext4.rb +304 -0
- data/lib/fs/MiqFS/modules/Ext4Probe.rb +25 -0
- data/lib/fs/MiqFS/modules/Fat32.rb +318 -0
- data/lib/fs/MiqFS/modules/Fat32Probe.rb +30 -0
- data/lib/fs/MiqFS/modules/HFSProbe.rb +18 -0
- data/lib/fs/MiqFS/modules/Iso9660.rb +293 -0
- data/lib/fs/MiqFS/modules/Iso9660Probe.rb +18 -0
- data/lib/fs/MiqFS/modules/LocalFS.rb +105 -0
- data/lib/fs/MiqFS/modules/NTFS.rb +287 -0
- data/lib/fs/MiqFS/modules/NTFSProbe.rb +21 -0
- data/lib/fs/MiqFS/modules/NativeFS.rb +155 -0
- data/lib/fs/MiqFS/modules/ReFSProbe.rb +17 -0
- data/lib/fs/MiqFS/modules/RealFS.rb +79 -0
- data/lib/fs/MiqFS/modules/RealFSProbe.rb +6 -0
- data/lib/fs/MiqFS/modules/Reiser4Probe.rb +18 -0
- data/lib/fs/MiqFS/modules/ReiserFS.rb +315 -0
- data/lib/fs/MiqFS/modules/ReiserFSProbe.rb +42 -0
- data/lib/fs/MiqFS/modules/UnionFSProbe.rb +18 -0
- data/lib/fs/MiqFS/modules/WebDAV.rb +127 -0
- data/lib/fs/MiqFS/modules/WebDAVFile.rb +68 -0
- data/lib/fs/MiqFS/modules/XFS.rb +300 -0
- data/lib/fs/MiqFS/modules/XFSProbe.rb +26 -0
- data/lib/fs/MiqFS/modules/ZFSProbe.rb +18 -0
- data/lib/fs/MiqFS/test.rb +59 -0
- data/lib/fs/MiqFsUtil.rb +383 -0
- data/lib/fs/MiqMountManager.rb +209 -0
- data/lib/fs/MiqNativeMountManager.rb +101 -0
- data/lib/fs/MountManagerProbe.rb +29 -0
- data/lib/fs/ReiserFS/block.rb +209 -0
- data/lib/fs/ReiserFS/directory.rb +136 -0
- data/lib/fs/ReiserFS/directory_entry.rb +140 -0
- data/lib/fs/ReiserFS/file_data.rb +111 -0
- data/lib/fs/ReiserFS/superblock.rb +140 -0
- data/lib/fs/ReiserFS/utils.rb +95 -0
- data/lib/fs/VimDatastoreFS/VimDatastoreFS.rb +192 -0
- data/lib/fs/ext3/alloc_bitmap.rb +38 -0
- data/lib/fs/ext3/block_pointers_path.rb +130 -0
- data/lib/fs/ext3/directory.rb +51 -0
- data/lib/fs/ext3/directory_entry.rb +67 -0
- data/lib/fs/ext3/ex_attrib_header.rb +14 -0
- data/lib/fs/ext3/ex_attrib_name.rb +23 -0
- data/lib/fs/ext3/file_data.rb +130 -0
- data/lib/fs/ext3/group_descriptor_entry.rb +65 -0
- data/lib/fs/ext3/group_descriptor_table.rb +54 -0
- data/lib/fs/ext3/hash_tree_entry.rb +18 -0
- data/lib/fs/ext3/hash_tree_header.rb +15 -0
- data/lib/fs/ext3/inode.rb +228 -0
- data/lib/fs/ext3/posix_acl_entry.rb +29 -0
- data/lib/fs/ext3/posix_acl_header.rb +11 -0
- data/lib/fs/ext3/superblock.rb +406 -0
- data/lib/fs/ext3/test/tc_Ext3BlockPointersPath.rb +74 -0
- data/lib/fs/ext4/alloc_bitmap.rb +38 -0
- data/lib/fs/ext4/directory.rb +87 -0
- data/lib/fs/ext4/directory_entry.rb +77 -0
- data/lib/fs/ext4/ex_attrib_header.rb +14 -0
- data/lib/fs/ext4/ex_attrib_name.rb +23 -0
- data/lib/fs/ext4/extent.rb +35 -0
- data/lib/fs/ext4/extent_header.rb +40 -0
- data/lib/fs/ext4/extent_index.rb +33 -0
- data/lib/fs/ext4/group_descriptor_entry.rb +69 -0
- data/lib/fs/ext4/group_descriptor_table.rb +54 -0
- data/lib/fs/ext4/hash_tree_entry.rb +58 -0
- data/lib/fs/ext4/hash_tree_header.rb +35 -0
- data/lib/fs/ext4/inode.rb +465 -0
- data/lib/fs/ext4/posix_acl_entry.rb +29 -0
- data/lib/fs/ext4/posix_acl_header.rb +11 -0
- data/lib/fs/ext4/superblock.rb +412 -0
- data/lib/fs/fat32/boot_sect.rb +379 -0
- data/lib/fs/fat32/directory.rb +222 -0
- data/lib/fs/fat32/directory_entry.rb +540 -0
- data/lib/fs/fat32/file_data.rb +128 -0
- data/lib/fs/iso9660/boot_sector.rb +170 -0
- data/lib/fs/iso9660/directory.rb +90 -0
- data/lib/fs/iso9660/directory_entry.rb +147 -0
- data/lib/fs/iso9660/file_data.rb +78 -0
- data/lib/fs/iso9660/rock_ridge.rb +329 -0
- data/lib/fs/iso9660/util.rb +57 -0
- data/lib/fs/modules/LinuxMount.rb +300 -0
- data/lib/fs/modules/LinuxMountProbe.rb +29 -0
- data/lib/fs/modules/WinMount.rb +97 -0
- data/lib/fs/modules/WinMountProbe.rb +24 -0
- data/lib/fs/ntfs/attrib_attribute_list.rb +131 -0
- data/lib/fs/ntfs/attrib_bitmap.rb +26 -0
- data/lib/fs/ntfs/attrib_data.rb +74 -0
- data/lib/fs/ntfs/attrib_file_name.rb +110 -0
- data/lib/fs/ntfs/attrib_header.rb +194 -0
- data/lib/fs/ntfs/attrib_index_allocation.rb +19 -0
- data/lib/fs/ntfs/attrib_index_root.rb +247 -0
- data/lib/fs/ntfs/attrib_object_id.rb +40 -0
- data/lib/fs/ntfs/attrib_standard_information.rb +107 -0
- data/lib/fs/ntfs/attrib_type.rb +49 -0
- data/lib/fs/ntfs/attrib_volume_information.rb +53 -0
- data/lib/fs/ntfs/attrib_volume_name.rb +31 -0
- data/lib/fs/ntfs/boot_sect.rb +253 -0
- data/lib/fs/ntfs/data_run.rb +358 -0
- data/lib/fs/ntfs/directory_index_node.rb +114 -0
- data/lib/fs/ntfs/index_node_header.rb +69 -0
- data/lib/fs/ntfs/index_record_header.rb +85 -0
- data/lib/fs/ntfs/mft_entry.rb +288 -0
- data/lib/fs/ntfs/utils.rb +43 -0
- data/lib/fs/test/camcorder_fs_test.rb +108 -0
- data/lib/fs/test/collect_files_direct.yaml +22 -0
- data/lib/fs/test/collect_files_in.yaml +24 -0
- data/lib/fs/test/collect_files_in_nc.yaml +22 -0
- data/lib/fs/test/collect_files_out.yaml +6 -0
- data/lib/fs/test/collect_files_rm.yaml +6 -0
- data/lib/fs/test/copyTest.rb +126 -0
- data/lib/fs/test/fsTest.rb +87 -0
- data/lib/fs/test/updateTest.rb +184 -0
- data/lib/fs/xfs/allocation_group.rb +160 -0
- data/lib/fs/xfs/bmap_btree_block.rb +125 -0
- data/lib/fs/xfs/bmap_btree_record.rb +80 -0
- data/lib/fs/xfs/bmap_btree_root_node.rb +72 -0
- data/lib/fs/xfs/directory.rb +133 -0
- data/lib/fs/xfs/directory2_data_header.rb +27 -0
- data/lib/fs/xfs/directory3_data_header.rb +34 -0
- data/lib/fs/xfs/directory_block_tail.rb +22 -0
- data/lib/fs/xfs/directory_data_header.rb +46 -0
- data/lib/fs/xfs/directory_entry.rb +106 -0
- data/lib/fs/xfs/inode.rb +532 -0
- data/lib/fs/xfs/inode_map.rb +100 -0
- data/lib/fs/xfs/short_form_directory_entry.rb +91 -0
- data/lib/fs/xfs/short_form_header.rb +44 -0
- data/lib/fs/xfs/superblock.rb +556 -0
- data/lib/lib/tasks/azure.rake +52 -0
- data/lib/manageiq/smartstate/version.rb +5 -0
- data/lib/manageiq/smartstate.rb +7 -0
- data/lib/manageiq-smartstate.rb +1 -0
- data/lib/metadata/MIQExtract/MIQExtract.rb +297 -0
- data/lib/metadata/MIQExtract/test/extractTest.rb +41 -0
- data/lib/metadata/MIQExtract/test/full_extract_test.rb +68 -0
- data/lib/metadata/ScanProfile/HostScanItem.rb +4 -0
- data/lib/metadata/ScanProfile/HostScanProfile.rb +4 -0
- data/lib/metadata/ScanProfile/HostScanProfiles.rb +41 -0
- data/lib/metadata/ScanProfile/ScanItemBase.rb +63 -0
- data/lib/metadata/ScanProfile/ScanProfileBase.rb +51 -0
- data/lib/metadata/ScanProfile/ScanProfilesBase.rb +60 -0
- data/lib/metadata/ScanProfile/VmScanItem.rb +4 -0
- data/lib/metadata/ScanProfile/VmScanProfile.rb +4 -0
- data/lib/metadata/ScanProfile/VmScanProfiles.rb +38 -0
- data/lib/metadata/ScanProfile/modules/HostScanItemFile.rb +51 -0
- data/lib/metadata/ScanProfile/modules/HostScanItemNteventlog.rb +84 -0
- data/lib/metadata/ScanProfile/modules/VmScanItemFile.rb +39 -0
- data/lib/metadata/ScanProfile/modules/VmScanItemNteventlog.rb +34 -0
- data/lib/metadata/ScanProfile/modules/VmScanItemRegistry.rb +64 -0
- data/lib/metadata/VMMount/VMMount.rb +81 -0
- data/lib/metadata/VMMount/VMPlatformMount.rb +18 -0
- data/lib/metadata/VMMount/VMPlatformMountLinux.rb +75 -0
- data/lib/metadata/VMMount/VMPlatformMountWin.rb +13 -0
- data/lib/metadata/VmConfig/GetNativeCfg.rb +45 -0
- data/lib/metadata/VmConfig/VmConfig.rb +947 -0
- data/lib/metadata/VmConfig/cfgConfig.rb +45 -0
- data/lib/metadata/VmConfig/ovfConfig.rb +99 -0
- data/lib/metadata/VmConfig/test/GetVMwareCfgTest.rb +40 -0
- data/lib/metadata/VmConfig/vmcConfig.rb +116 -0
- data/lib/metadata/VmConfig/vmtxConfig.rb +4 -0
- data/lib/metadata/VmConfig/vmxConfig.rb +162 -0
- data/lib/metadata/VmConfig/xmlConfig.rb +79 -0
- data/lib/metadata/VmConfig/xmlMsHyperVConfig.rb +41 -0
- data/lib/metadata/linux/InitProcHash.rb +632 -0
- data/lib/metadata/linux/LinuxInitProcs.rb +142 -0
- data/lib/metadata/linux/LinuxOSInfo.rb +237 -0
- data/lib/metadata/linux/LinuxPackages.rb +209 -0
- data/lib/metadata/linux/LinuxSystemd.rb +130 -0
- data/lib/metadata/linux/LinuxUsers.rb +289 -0
- data/lib/metadata/linux/LinuxUtils.rb +197 -0
- data/lib/metadata/linux/MiqConaryPackages.rb +41 -0
- data/lib/metadata/linux/MiqRpmPackages.rb +160 -0
- data/lib/metadata/linux/test/Name +0 -0
- data/lib/metadata/linux/test/Packages +0 -0
- data/lib/metadata/linux/test/rpoTest.rb +5 -0
- data/lib/metadata/linux/test/tc_LinuxUtils.rb +4157 -0
- data/lib/metadata/util/event_log_filter.rb +61 -0
- data/lib/metadata/util/md5deep.rb +280 -0
- data/lib/metadata/util/win32/Win32Accounts.rb +764 -0
- data/lib/metadata/util/win32/Win32EventLog.rb +743 -0
- data/lib/metadata/util/win32/Win32Services.rb +86 -0
- data/lib/metadata/util/win32/Win32Software.rb +326 -0
- data/lib/metadata/util/win32/Win32System.rb +333 -0
- data/lib/metadata/util/win32/boot_info_win.rb +59 -0
- data/lib/metadata/util/win32/fleece_hives.rb +220 -0
- data/lib/metadata/util/win32/ms-registry.rb +650 -0
- data/lib/metadata/util/win32/peheader.rb +868 -0
- data/lib/metadata/util/win32/remote-registry.rb +142 -0
- data/lib/metadata/util/win32/system_path_win.rb +103 -0
- data/lib/metadata/util/win32/versioninfo.rb +17 -0
- data/manageiq-smartstate.gemspec +35 -0
- metadata +486 -0
@@ -0,0 +1,868 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
require 'binary_struct'
|
6
|
+
require 'util/miq-unicode'
|
7
|
+
|
8
|
+
# Notes:
|
9
|
+
# The peheader object member 'icons' is an array of icons in the file. Sub 0 is the application
|
10
|
+
# icon, 1 is usually the document icon. The format is the same as an .ico file. The simple test
|
11
|
+
# writes found icons to the root dir as icon0.ico, icon1.ico, etc. Any icon editor will be able
|
12
|
+
# to open them and display each resolution contained in the icon (if more than one).
|
13
|
+
|
14
|
+
class PEheader
|
15
|
+
IMAGE_NT_SIGNATURE = "PE\0\0"
|
16
|
+
IMAGE_DOS_SIGNATURE = "MZ"
|
17
|
+
|
18
|
+
attr_reader :imports, :icons, :messagetables, :versioninfo
|
19
|
+
|
20
|
+
def initialize(path)
|
21
|
+
@fname = path
|
22
|
+
@dataDirs = []
|
23
|
+
@sectionTable = []
|
24
|
+
@baseResDir = nil
|
25
|
+
|
26
|
+
if path.class != String
|
27
|
+
@fHnd = path
|
28
|
+
fileseek(0, 'init')
|
29
|
+
fBuf = fileread(2)
|
30
|
+
else
|
31
|
+
# Do some basic file validation
|
32
|
+
raise Errno::ENOENT, "File [#{@fname}] does not exist." if File.exist?(@fname) == false
|
33
|
+
raise "File [#{@fname}] is empty." if File.zero?(@fname)
|
34
|
+
|
35
|
+
# Open file and read contents into buffer
|
36
|
+
@fHnd = File.open(@fname, "rb")
|
37
|
+
fBuf = fileread(2)
|
38
|
+
@fHnd.close
|
39
|
+
end
|
40
|
+
|
41
|
+
# Check for the MZ header
|
42
|
+
raise "Version Information header not found in file [#{@fname}]" unless fBuf[0..1] == IMAGE_DOS_SIGNATURE
|
43
|
+
|
44
|
+
readPE
|
45
|
+
end
|
46
|
+
|
47
|
+
def readPE
|
48
|
+
if @fname.class != String
|
49
|
+
@fHnd = @fname
|
50
|
+
fileseek(0, 'readPE')
|
51
|
+
else
|
52
|
+
# Open file
|
53
|
+
@fHnd = File.open(@fname, "rb")
|
54
|
+
end
|
55
|
+
|
56
|
+
# Read contents into buffer
|
57
|
+
# TODO: determine the proper amount of data to load here
|
58
|
+
@fBuf = fileread(10240)
|
59
|
+
|
60
|
+
# Read offsets for next string
|
61
|
+
dhHash = IMAGE_DOS_HEADER.decode(@fBuf)
|
62
|
+
offset = dhHash['e_lfanew'] # Offset to PE header
|
63
|
+
raise "PE header not found in file [#{@fname}]" unless @fBuf[offset...offset + 4] == IMAGE_NT_SIGNATURE
|
64
|
+
offset += 4
|
65
|
+
fhHash = IMAGE_FILE_HEADER.decode(@fBuf[offset..-1])
|
66
|
+
offset += SIZEOF_IMAGE_FILE_HEADER
|
67
|
+
|
68
|
+
@is64Bit = fhHash['SizeOfOptionalHeader'] == IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
|
69
|
+
@IMAGE_OPTIONAL_HEADER = (@is64Bit == true) ? IMAGE_OPTIONAL_HEADER64 : IMAGE_OPTIONAL_HEADER32
|
70
|
+
|
71
|
+
# Commented out the following, since it is not currently being used.
|
72
|
+
# ohHash = @IMAGE_OPTIONAL_HEADER.decode(@fBuf[offset, @IMAGE_OPTIONAL_HEADER.size])
|
73
|
+
|
74
|
+
# Read all the data directories & section table.
|
75
|
+
offset = getDataDirs(@fBuf, offset)
|
76
|
+
offset = getSectionTable(@fBuf, fhHash, offset)
|
77
|
+
end
|
78
|
+
|
79
|
+
# These file methods are here to assist in debugging
|
80
|
+
def fileseek(offset = 0, _message = nil)
|
81
|
+
# st = Time.now
|
82
|
+
@fHnd.seek(offset, IO::SEEK_SET)
|
83
|
+
# $log.warn "seek time [#{Time.now-st}] from [#{message}]" if $log
|
84
|
+
end
|
85
|
+
|
86
|
+
def fileread(length)
|
87
|
+
# st = Time.now
|
88
|
+
data = @fHnd.read(length)
|
89
|
+
# $log.warn "read time [#{Time.now-st}]" if $log
|
90
|
+
data
|
91
|
+
end
|
92
|
+
|
93
|
+
def imports
|
94
|
+
@import_array ||= getImports
|
95
|
+
end
|
96
|
+
|
97
|
+
def icons
|
98
|
+
@icon_array ||= getIcons(@fBuf)
|
99
|
+
end
|
100
|
+
|
101
|
+
def messagetables
|
102
|
+
@messagetable_hash ||= getMessagetables
|
103
|
+
end
|
104
|
+
|
105
|
+
def versioninfo
|
106
|
+
@versioninfo_array ||= getVersioninfo
|
107
|
+
end
|
108
|
+
|
109
|
+
# //////////////////////////////////////////////////////////////////////////
|
110
|
+
# //
|
111
|
+
|
112
|
+
def getDataDirs(fBuf, offset)
|
113
|
+
offset += @IMAGE_OPTIONAL_HEADER.size
|
114
|
+
IMAGE_NUMBEROF_DIRECTORY_ENTRIES.times do
|
115
|
+
ddHash = IMAGE_DATA_DIRECTORY.decode(fBuf[offset..-1])
|
116
|
+
offset += SIZEOF_IMAGE_DATA_DIRECTORY
|
117
|
+
@dataDirs.push(ddHash)
|
118
|
+
end
|
119
|
+
offset
|
120
|
+
end
|
121
|
+
|
122
|
+
def getSectionTable(fBuf, fhHash, offset)
|
123
|
+
fhHash['NumberOfSections'].times do
|
124
|
+
shHash = IMAGE_SECTION_HEADER.decode(fBuf[offset..-1])
|
125
|
+
offset += SIZEOF_IMAGE_SECTION_HEADER
|
126
|
+
@sectionTable.push(shHash)
|
127
|
+
end
|
128
|
+
offset
|
129
|
+
end
|
130
|
+
|
131
|
+
def getImports
|
132
|
+
imports_libs = []
|
133
|
+
import = @dataDirs[IMAGE_DIRECTORY_ENTRY_IMPORT]
|
134
|
+
import[:offset] = import[:virtualAddress]
|
135
|
+
if import[:offset] != 0
|
136
|
+
import[:offset] = adjustAddress(import[:offset])
|
137
|
+
fileseek(import[:offset], 'getImports')
|
138
|
+
data = fileread(import[:size])
|
139
|
+
offset = 0
|
140
|
+
loop do
|
141
|
+
iiHash = IMAGE_IMPORT_DESCRIPTOR.decode(data[offset..-1])
|
142
|
+
break if iiHash['Name'] == 0
|
143
|
+
offset += SIZEOF_IMAGE_IMPORT_DESCRIPTOR
|
144
|
+
iiHash['Name'] = adjustAddress(iiHash['Name']) - import[:offset]
|
145
|
+
|
146
|
+
# Check if we have enough data. This happens if the import data only contains pointers
|
147
|
+
if (data.length <= iiHash['Name'])
|
148
|
+
size = iiHash['Name'] - data.length + 4096
|
149
|
+
data += fileread(size)
|
150
|
+
end
|
151
|
+
|
152
|
+
nameEnd = iiHash['Name'] + data[iiHash['Name']..-1].index("\0") - 1
|
153
|
+
imports_libs.push(data[iiHash['Name']..nameEnd].downcase)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
imports_libs
|
157
|
+
end
|
158
|
+
|
159
|
+
def getIcons(fBuf)
|
160
|
+
iconEntries = getRawIcons(fBuf)
|
161
|
+
grpIcons = getIconDirEntries(fBuf)
|
162
|
+
assembleIcons(iconEntries, grpIcons)
|
163
|
+
end
|
164
|
+
|
165
|
+
def getRawIcons(_fBuf)
|
166
|
+
# Read raw icons.
|
167
|
+
iconEntries = []
|
168
|
+
get_resources_by_type(RT_ICON) do |icon_rsc|
|
169
|
+
ent = icon_rsc[:data]
|
170
|
+
ent[:offset] = adjustAddress(ent[:offsetToData])
|
171
|
+
fileseek(ent[:offset], 'getRawIcons')
|
172
|
+
icon_rsc[:icon] = fileread(ent[:size])
|
173
|
+
iconEntries << icon_rsc
|
174
|
+
end
|
175
|
+
iconEntries
|
176
|
+
end
|
177
|
+
|
178
|
+
def getIconDirEntries(_fBuf)
|
179
|
+
# Read icon directory.
|
180
|
+
grpIcons = []
|
181
|
+
# iconDirEntries = getDataEntries(RT_GROUP_ICON, fBuf)
|
182
|
+
get_resources_by_type(RT_GROUP_ICON) do |icon_rsc|
|
183
|
+
ent = icon_rsc[:data]
|
184
|
+
ent[:offset] = adjustAddress(ent[:offsetToData])
|
185
|
+
fileseek(ent[:offset], 'getIconDirEntries')
|
186
|
+
iconDir = fileread(ent[:size])
|
187
|
+
iconDir = GRPICONDIR.decode(iconDir)
|
188
|
+
grpIconDirEntries = []
|
189
|
+
0.upto(iconDir[:idCount] - 1) { |i| grpIconDirEntries << GRPICONDIRENTRY.decode(iconDir[:data][i * SIZEOF_GRPICONDIRENTRY, SIZEOF_GRPICONDIRENTRY]) }
|
190
|
+
grpIcons << grpIconDirEntries
|
191
|
+
end
|
192
|
+
grpIcons
|
193
|
+
end
|
194
|
+
|
195
|
+
def assembleIcons(iconEntries, grpIcons)
|
196
|
+
# For each major sub in grpIcons, construct an .ico blob.
|
197
|
+
icons = []
|
198
|
+
0.upto(grpIcons.size - 1) do |fileIdx|
|
199
|
+
# Write icon directory.
|
200
|
+
baseOffset = 16 * grpIcons[fileIdx].size + 6
|
201
|
+
thisOffset = 0
|
202
|
+
ico = StringIO.new
|
203
|
+
ico.write([0].pack('S')) # idReserved
|
204
|
+
ico.write([1].pack('S')) # idType
|
205
|
+
ico.write([grpIcons[fileIdx].size].pack('S')) # idCount
|
206
|
+
0.upto(grpIcons[fileIdx].size - 1) do |iconIdx|
|
207
|
+
icon = grpIcons[fileIdx][iconIdx]
|
208
|
+
# Write icon dir entry.
|
209
|
+
ico.write([icon[:bWidth]].pack('C'))
|
210
|
+
ico.write([icon[:bHeight]].pack('C'))
|
211
|
+
ico.write([icon[:bColorCount]].pack('C'))
|
212
|
+
ico.write([0].pack('C'))
|
213
|
+
ico.write([icon[:wPlanes]].pack('S'))
|
214
|
+
ico.write([icon[:wBitCount]].pack('S'))
|
215
|
+
ico.write([icon[:dwBytesInRes]].pack('L'))
|
216
|
+
ico.write([baseOffset + thisOffset].pack('L'))
|
217
|
+
thisOffset += icon[:dwBytesInRes]
|
218
|
+
end
|
219
|
+
# Write icon data.
|
220
|
+
0.upto(grpIcons[fileIdx].size - 1) { |iconIdx| ico.write(getIconById(iconEntries, grpIcons[fileIdx][iconIdx][:nID])) }
|
221
|
+
# Save it as a string.
|
222
|
+
ico.rewind
|
223
|
+
icons << ico.read
|
224
|
+
end
|
225
|
+
icons
|
226
|
+
end
|
227
|
+
|
228
|
+
# Find a particular raw icon.
|
229
|
+
def getIconById(icons, id)
|
230
|
+
icons.each { |icon| return icon[:icon] if icon[:rsc_id] == id }
|
231
|
+
nil
|
232
|
+
end
|
233
|
+
|
234
|
+
def getMessagetables(requested_locale = 0x0409)
|
235
|
+
# Read message table resources.
|
236
|
+
messagetables = {}
|
237
|
+
get_resources_by_type(RT_MESSAGETABLE, requested_locale) do |msg_resource|
|
238
|
+
# Get the block directory for this messagetable.
|
239
|
+
msg_data = msg_resource[:data]
|
240
|
+
offset = adjustAddress(msg_data[:offsetToData]) - @dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE][:offset]
|
241
|
+
blkdir = MESSAGE_RESOURCE_DATA.decode(@dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE][:data][offset, msg_data[:size]])
|
242
|
+
0.upto(blkdir[:numberOfBlocks] - 1) do |i|
|
243
|
+
# Break out each block.
|
244
|
+
blk = MESSAGE_RESOURCE_BLOCK.decode(blkdir[:data][i * SIZEOF_MRB, SIZEOF_MRB])
|
245
|
+
adrs = blk[:offsetToEntries] - 4
|
246
|
+
|
247
|
+
# Grab the block's strings.
|
248
|
+
blk[:loId].upto(blk[:hiId]) do |idx|
|
249
|
+
ent1 = MESSAGE_RESOURCE_ENTRY.decode(blkdir[:data][adrs, SIZEOF_MRE])
|
250
|
+
if ent1[:length] > 0
|
251
|
+
len = ent1[:length] - SIZEOF_MRE
|
252
|
+
str = blkdir[:data][adrs + SIZEOF_MRE, len]
|
253
|
+
(ent1[:flags] == MESSAGE_RESOURCE_UNICODE) ? str.UnicodeToUtf8! : str.AsciiToUtf8!
|
254
|
+
str.gsub!(/\000/, "")
|
255
|
+
messagetables[idx] = str
|
256
|
+
adrs += len
|
257
|
+
end
|
258
|
+
adrs += SIZEOF_MRE
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
messagetables
|
263
|
+
end
|
264
|
+
|
265
|
+
# Get versioninfo resource.
|
266
|
+
def getVersioninfo(requested_locale = 0x0409)
|
267
|
+
aVersioninfoHash = {}
|
268
|
+
get_resources_by_type(RT_VERSION, requested_locale) do |versionEntry|
|
269
|
+
ent = versionEntry[:data]
|
270
|
+
offset = adjustAddress(ent[:offsetToData]) - @dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE][:offset]
|
271
|
+
versionInfo = @dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE][:data][offset, ent[:size]]
|
272
|
+
# versionInfo is a VS_FIXEDFILEINFO structure followed by StringFileInfo.
|
273
|
+
aVersioninfoHash = getVersionInfoHash(versionInfo)
|
274
|
+
end
|
275
|
+
aVersioninfoHash
|
276
|
+
end
|
277
|
+
|
278
|
+
# Walk the resource directories and collect all the directories and resource pointers
|
279
|
+
def getDataEntries(fBuf, rsc_id = nil)
|
280
|
+
result = {}
|
281
|
+
getBaseResDir(fBuf) { |baseResDir| dumpResourceDirectory(baseResDir, 0, result, rsc_id) }
|
282
|
+
result
|
283
|
+
end
|
284
|
+
|
285
|
+
def dumpResourceDirectory(resDir, level, data_hash, rsc_id = nil)
|
286
|
+
resDirEntry = getResourceDirectoryEntry(resDir)
|
287
|
+
|
288
|
+
# Process each entry in the directory.
|
289
|
+
# Note: Named entries are listed first.
|
290
|
+
1.upto(resDir[:numberOfNamedEntries]) do
|
291
|
+
dumpResourceEntry(resDirEntry, level + 1, data_hash, rsc_id)
|
292
|
+
resDirEntry = getNextResourceDirectoryEntry(resDirEntry)
|
293
|
+
end
|
294
|
+
|
295
|
+
1.upto(resDir[:numberOfIdEntries]) do
|
296
|
+
dumpResourceEntry(resDirEntry, level + 1, data_hash, rsc_id)
|
297
|
+
resDirEntry = getNextResourceDirectoryEntry(resDirEntry)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def dumpResourceEntry(resDirEntry, level, data_hash, rsc_id)
|
302
|
+
# 1.upto(level) {print " "}
|
303
|
+
|
304
|
+
resDirEntry[:name] = getResourceDirectoryEntryName(resDirEntry)
|
305
|
+
resDirEntry[:level] = level
|
306
|
+
if resDirEntry[:isDir]
|
307
|
+
|
308
|
+
# Filter by resource type so we do not process every available resource
|
309
|
+
return if level == 1 && rsc_id && resDirEntry[:name] != rsc_id
|
310
|
+
|
311
|
+
resDir = getResourceDirectory(resDirEntry)
|
312
|
+
resDirEntry[:numberOfIdEntries] = resDir[:numberOfIdEntries]
|
313
|
+
resDirEntry[:numberOfNamedEntries] = resDir[:numberOfNamedEntries]
|
314
|
+
# puts "DIR: #{resDirEntry.inspect}"
|
315
|
+
|
316
|
+
data_hash[resDirEntry[:name]] = resDirEntry
|
317
|
+
resDirEntry[:children] = {}
|
318
|
+
|
319
|
+
dumpResourceDirectory(resDir, level, resDirEntry[:children], rsc_id)
|
320
|
+
else
|
321
|
+
data_hash[resDirEntry[:name]] = resDirEntry
|
322
|
+
resDirEntry[:data] = IMAGE_RESOURCE_DATA_ENTRY.decode(@dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE][:data][resDirEntry[:offsetToData]..-1])
|
323
|
+
# puts "RSC: #{resDirEntry.inspect}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def getResourceDirectory(resDirEntry)
|
328
|
+
offset = resDirEntry[:offsetToData] & 0x7fffffff
|
329
|
+
resDir = IMAGE_RESOURCE_DIRECTORY.decode(@dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE][:data][offset..-1])
|
330
|
+
resDir[:offset_into_data] = offset
|
331
|
+
resDir
|
332
|
+
end
|
333
|
+
|
334
|
+
def getResourceDirectoryEntry(resDir)
|
335
|
+
getNextResourceResourceEntry(resDir, IMAGE_RESOURCE_DIRECTORY_ENTRY, SIZEOF_IMAGE_RESOURCE_DIRECTORY)
|
336
|
+
end
|
337
|
+
|
338
|
+
def getNextResourceDirectoryEntry(resDirEntry)
|
339
|
+
getNextResourceResourceEntry(resDirEntry, IMAGE_RESOURCE_DIRECTORY_ENTRY, SIZEOF_IMAGE_RESOURCE_DIRECTORY_ENTRY)
|
340
|
+
end
|
341
|
+
|
342
|
+
def getNextResourceResourceEntry(resEntry, rsc_type, size)
|
343
|
+
offset = resEntry[:offset_into_data] + size
|
344
|
+
entry = rsc_type.decode(@dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE][:data][offset..-1])
|
345
|
+
entry[:offset_into_data] = offset
|
346
|
+
entry[:isDir] = bit?(entry[:offsetToData], 31)
|
347
|
+
entry
|
348
|
+
end
|
349
|
+
|
350
|
+
def getBaseResDir(_fBuf)
|
351
|
+
if @baseResDir.nil?
|
352
|
+
rsc = @dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE]
|
353
|
+
rsc[:offset] = rsc[:virtualAddress]
|
354
|
+
unless rsc[:offset].zero?
|
355
|
+
rsc[:offset] = adjustAddress(rsc[:offset])
|
356
|
+
# Read in the resource part the file
|
357
|
+
fileseek(rsc[:offset], 'getBaseResDir')
|
358
|
+
rsc[:data] = fileread(rsc[:size])
|
359
|
+
@baseResDir = IMAGE_RESOURCE_DIRECTORY.decode(rsc[:data][0, SIZEOF_IMAGE_RESOURCE_DIRECTORY])
|
360
|
+
@baseResDir[:offset_into_data] = 0
|
361
|
+
end
|
362
|
+
end
|
363
|
+
yield(@baseResDir) unless @baseResDir.nil?
|
364
|
+
end
|
365
|
+
|
366
|
+
def get_resources_by_type(rt, locale_id = nil)
|
367
|
+
if (rsc = getDataEntries(@fBuf, rt)[rt])
|
368
|
+
resources = []
|
369
|
+
find_all_resources(rsc[:children]) { |r| resources << r }
|
370
|
+
return if resources.empty?
|
371
|
+
|
372
|
+
# Finding a resource is often by locale. If we do not find the requested
|
373
|
+
# locale then return the first one.
|
374
|
+
unless locale_id.nil?
|
375
|
+
local_rsc = resources.detect { |r| r[:lang_id] == locale_id }
|
376
|
+
resources = local_rsc.nil? ? [resources.first] : [local_rsc]
|
377
|
+
end
|
378
|
+
|
379
|
+
# Yield the resource data to the caller
|
380
|
+
resources.each { |r| yield(r) }
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def find_all_resources(rsc, rsc_id = nil, &blk)
|
385
|
+
# Resource Directory Levels:
|
386
|
+
# 1 = Resource Type
|
387
|
+
# 2 = Resource Identifier
|
388
|
+
# 3 = Resource Langauge ID
|
389
|
+
rsc.each do |lang_id, item|
|
390
|
+
if item[:isDir]
|
391
|
+
rsc_id = item[:name] if item[:level] == 2
|
392
|
+
find_all_resources(item[:children], rsc_id, &blk)
|
393
|
+
else
|
394
|
+
item[:rsc_id] = rsc_id
|
395
|
+
item[:lang_id] = lang_id
|
396
|
+
yield(item)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def getResourceDirectoryEntryName(resDirEntry)
|
402
|
+
return resDirEntry[:name] unless bit?(resDirEntry[:name], 31)
|
403
|
+
|
404
|
+
# The low 30 bits of the 'Name' member is an offset to an IMAGE_RESOURCE_DIRECTORY_STRING_U struct.
|
405
|
+
str = ""
|
406
|
+
ptr = (resDirEntry[:name] & 0x7fffffff)
|
407
|
+
len = @dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE][:data][ptr, 2].unpack('S')[0]
|
408
|
+
str = @dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE][:data][ptr + 2, len * 2]
|
409
|
+
str.UnicodeToUtf8!
|
410
|
+
end
|
411
|
+
|
412
|
+
def adjustAddress(rva)
|
413
|
+
@sectionTable.each do |s|
|
414
|
+
# Is the RVA within this section?
|
415
|
+
if (rva >= s[:virtualAddress]) && (rva < (s[:virtualAddress] + s[:VirtualSize]))
|
416
|
+
delta = s[:virtualAddress] - s[:PointerToRawData]
|
417
|
+
return rva - delta
|
418
|
+
end
|
419
|
+
end
|
420
|
+
nil
|
421
|
+
end
|
422
|
+
|
423
|
+
def getImportList
|
424
|
+
return nil if imports.nil?
|
425
|
+
unless imports.empty?
|
426
|
+
import_list = ""
|
427
|
+
imports.each { |i| import_list += i + ", " }
|
428
|
+
return import_list.rstrip.chomp(",")
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
def getVersionInfoHash(fBuf)
|
433
|
+
viHash = {}
|
434
|
+
|
435
|
+
# Find VS Version Info signature
|
436
|
+
idx = fBuf.index(VS_VERSION_INFO)
|
437
|
+
return viHash unless idx
|
438
|
+
# raise "Version Information header not found in file" unless idx
|
439
|
+
# $log.debug sprintf("Found at index: [0X%08X] (%d)", idx, idx)
|
440
|
+
|
441
|
+
# Reduce buffer to just the signature to the end of the file
|
442
|
+
fBuf = fBuf[idx..fBuf.length]
|
443
|
+
offset = 0
|
444
|
+
vhHash = VS_VERSION_INFO_HEADER.decode(fBuf[offset..(offset + SIZEOF_VS_VERSION_INFO_HEADER)])
|
445
|
+
|
446
|
+
# Create VersionInfo hash
|
447
|
+
viHash['FILEVERSION_HEADER'] = vhHash['fmajor'].to_s + "," + vhHash['fminor'].to_s + "," + vhHash['frev'].to_s + "," + vhHash['fbuild'].to_s
|
448
|
+
viHash['PRODUCTVERSION_HEADER'] = vhHash['pmajor'].to_s + "," + vhHash['pminor'].to_s + "," + vhHash['prev'].to_s + "," + vhHash['pbuild'].to_s
|
449
|
+
|
450
|
+
# Find the string file info signautre
|
451
|
+
idx = fBuf.index(STRINGFILEINFO)
|
452
|
+
return viHash unless idx
|
453
|
+
# raise "String File information header not found in file [#{fname}]" unless idx
|
454
|
+
|
455
|
+
offset = idx
|
456
|
+
viEnd = offset + SIZEOF_STRING_INFO_HEADER
|
457
|
+
viHash.merge!(STRING_INFO_HEADER.decode(fBuf[offset..viEnd]))
|
458
|
+
viHash['sig'].UnicodeToUtf8!.tr!("\0", "")
|
459
|
+
viHash['code_page'].UnicodeToUtf8!.tr!("\0", "")
|
460
|
+
viHash['lang'].UnicodeToUtf8!.tr!("\0", "")
|
461
|
+
|
462
|
+
# Read offsets for next string
|
463
|
+
offset = viEnd
|
464
|
+
vsHash = VERSION_STRING_HEADER.decode(fBuf[offset..offset + 6])
|
465
|
+
|
466
|
+
# Calculate the amount of version info data
|
467
|
+
offset_end = offset + viHash['data_length'] - SIZEOF_STRING_INFO_HEADER
|
468
|
+
|
469
|
+
while offset < offset_end
|
470
|
+
break unless vsHash['zero'] == 0
|
471
|
+
break if vsHash['zero'].nil? || vsHash['vlen'].nil? || vsHash['slen'].nil?
|
472
|
+
offset += SIZEOF_VERSION_STRING_HEADER
|
473
|
+
name_len = vsHash['slen'] - 4 - (vsHash['vlen'] * 2) - 2
|
474
|
+
name = fBuf[offset...offset + name_len]
|
475
|
+
offset += name_len
|
476
|
+
value_len = (vsHash['vlen'] * 2) - 2
|
477
|
+
value = fBuf[offset...offset + value_len]
|
478
|
+
break if name.nil? or value.nil? or name.empty?
|
479
|
+
name.UnicodeToUtf8!.delete!("\0")
|
480
|
+
# Do not allow spaces in the attribute names (will invalidate a XML file)
|
481
|
+
name.tr!(" ", "_")
|
482
|
+
value.UnicodeToUtf8!.delete!("\0")
|
483
|
+
# $log.debug "[#{name}] => [#{value}]"
|
484
|
+
viHash[name] = value
|
485
|
+
offset += value_len + (vsHash['vlen'] % 2 * 2)
|
486
|
+
|
487
|
+
# Read next offset header
|
488
|
+
vsHash = VERSION_STRING_HEADER.decode(fBuf[offset..offset + 6])
|
489
|
+
|
490
|
+
# This is a work-around. In case the offset to the next record is slightly off
|
491
|
+
unless vsHash['zero'] == 0
|
492
|
+
offset -= 2
|
493
|
+
# Read next offset header
|
494
|
+
vsHash = VERSION_STRING_HEADER.decode(fBuf[offset..offset + 6])
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
viHash
|
499
|
+
end
|
500
|
+
|
501
|
+
################################################################
|
502
|
+
# PE Header structures defined
|
503
|
+
################################################################
|
504
|
+
# From WINNT.H
|
505
|
+
#
|
506
|
+
# // Directory Entries
|
507
|
+
#
|
508
|
+
# // Export Directory
|
509
|
+
IMAGE_DIRECTORY_ENTRY_EXPORT = 0
|
510
|
+
# // Import Directory
|
511
|
+
IMAGE_DIRECTORY_ENTRY_IMPORT = 1
|
512
|
+
# // Resource Directory
|
513
|
+
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2
|
514
|
+
# // Exception Directory
|
515
|
+
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3
|
516
|
+
# // Security Directory
|
517
|
+
IMAGE_DIRECTORY_ENTRY_SECURITY = 4
|
518
|
+
# // Base Relocation Table
|
519
|
+
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5
|
520
|
+
# // Debug Directory
|
521
|
+
IMAGE_DIRECTORY_ENTRY_DEBUG = 6
|
522
|
+
# // Description String
|
523
|
+
IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7
|
524
|
+
# // Machine Value (MIPS GP)
|
525
|
+
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8
|
526
|
+
# // TLS Directory
|
527
|
+
IMAGE_DIRECTORY_ENTRY_TLS = 9
|
528
|
+
# // Load Configuration Directory
|
529
|
+
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10
|
530
|
+
|
531
|
+
IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
|
532
|
+
|
533
|
+
# From WinUser.h
|
534
|
+
# /*
|
535
|
+
# * Predefined Resource Types
|
536
|
+
# */
|
537
|
+
RT_CURSOR = 1
|
538
|
+
RT_BITMAP = 2
|
539
|
+
RT_ICON = 3
|
540
|
+
RT_MENU = 4
|
541
|
+
RT_DIALOG = 5
|
542
|
+
RT_STRING = 6
|
543
|
+
RT_FONTDIR = 7
|
544
|
+
RT_FONT = 8
|
545
|
+
RT_ACCELERATOR = 9
|
546
|
+
RT_RCDATA = 10
|
547
|
+
RT_MESSAGETABLE = 11
|
548
|
+
RT_GROUP_ICON = 14
|
549
|
+
RT_VERSION = 16
|
550
|
+
|
551
|
+
IMAGE_FILE_HEADER = BinaryStruct.new([
|
552
|
+
'v', 'Machine',
|
553
|
+
'v', 'NumberOfSections',
|
554
|
+
'V', 'TimeDateStamp',
|
555
|
+
'V', 'PointerToSymbolTable',
|
556
|
+
'V', 'NumberOfSymbols',
|
557
|
+
'v', 'SizeOfOptionalHeader',
|
558
|
+
'v', 'Characteristics',
|
559
|
+
])
|
560
|
+
SIZEOF_IMAGE_FILE_HEADER = IMAGE_FILE_HEADER.size
|
561
|
+
|
562
|
+
IMAGE_DOS_HEADER = BinaryStruct.new([ # // DOS .EXE header
|
563
|
+
'v', 'e_magic', # // Magic number
|
564
|
+
'v', 'e_cblp', # // Bytes on last page of file
|
565
|
+
'v', 'e_cp', # // Pages in file
|
566
|
+
'v', 'e_crlc', # // Relocations
|
567
|
+
'v', 'e_cparhdr', # // Size of header in paragraphs
|
568
|
+
'v', 'e_minalloc', # // Minimum extra paragraphs needed
|
569
|
+
'v', 'e_maxalloc', # // Maximum extra paragraphs needed
|
570
|
+
'v', 'e_ss', # // Initial (relative) SS value
|
571
|
+
'v', 'e_sp', # // Initial SP value
|
572
|
+
'v', 'e_csum', # // Checksum
|
573
|
+
'v', 'e_ip', # // Initial IP value
|
574
|
+
'v', 'e_cs', # // Initial (relative) CS value
|
575
|
+
'v', 'e_lfarlc', # // File address of relocation table
|
576
|
+
'v', 'e_ovno', # // Overlay number
|
577
|
+
'v', nil, # // Reserved words - e_res[4]
|
578
|
+
'v', nil, # // Reserved words - e_res[4]
|
579
|
+
'v', nil, # // Reserved words - e_res[4]
|
580
|
+
'v', nil, # // Reserved words - e_res[4]
|
581
|
+
'v', 'e_oemid', # // OEM identifier (for e_oeminfo)
|
582
|
+
'v', 'e_oeminfo', # // OEM information; e_oemid specific
|
583
|
+
'v', nil, # // Reserved words - e_res2[10]
|
584
|
+
'v', nil, # // Reserved words - e_res2[10]
|
585
|
+
'v', nil, # // Reserved words - e_res2[10]
|
586
|
+
'v', nil, # // Reserved words - e_res2[10]
|
587
|
+
'v', nil, # // Reserved words - e_res2[10]
|
588
|
+
'v', nil, # // Reserved words - e_res2[10]
|
589
|
+
'v', nil, # // Reserved words - e_res2[10]
|
590
|
+
'v', nil, # // Reserved words - e_res2[10]
|
591
|
+
'v', nil, # // Reserved words - e_res2[10]
|
592
|
+
'v', nil, # // Reserved words - e_res2[10]
|
593
|
+
'V', 'e_lfanew', # // File address of new exe header
|
594
|
+
])
|
595
|
+
SIZEOF_IMAGE_DOS_HEADER = IMAGE_DOS_HEADER.size
|
596
|
+
|
597
|
+
IMAGE_OPTIONAL_HEADER32 = BinaryStruct.new([
|
598
|
+
# //
|
599
|
+
# // Standard fields.
|
600
|
+
# //
|
601
|
+
|
602
|
+
'v', 'Magic',
|
603
|
+
'c', 'MajorLinkerVersion',
|
604
|
+
'c', 'MinorLinkerVersion',
|
605
|
+
'V', 'SizeOfCode',
|
606
|
+
'V', 'SizeOfInitializedData',
|
607
|
+
'V', 'SizeOfUninitializedData',
|
608
|
+
'V', 'AddressOfEntryPoint',
|
609
|
+
'V', 'BaseOfCode',
|
610
|
+
'V', 'BaseOfData',
|
611
|
+
#
|
612
|
+
# //
|
613
|
+
# // NT additional fields.
|
614
|
+
# //
|
615
|
+
#
|
616
|
+
'V', 'ImageBase',
|
617
|
+
'V', 'SectionAlignment',
|
618
|
+
'V', 'FileAlignment',
|
619
|
+
'v', 'MajorOperatingSystemVersion',
|
620
|
+
'v', 'MinorOperatingSystemVersion',
|
621
|
+
'v', 'MajorImageVersion',
|
622
|
+
'v', 'MinorImageVersion',
|
623
|
+
'v', 'MajorSubsystemVersion',
|
624
|
+
'v', 'MinorSubsystemVersion',
|
625
|
+
'V', 'Win32VersionValue',
|
626
|
+
'V', 'SizeOfImage',
|
627
|
+
'V', 'SizeOfHeaders',
|
628
|
+
'V', 'CheckSum',
|
629
|
+
'v', 'Subsystem',
|
630
|
+
'v', 'DllCharacteristics',
|
631
|
+
'V', 'SizeOfStackReserve',
|
632
|
+
'V', 'SizeOfStackCommit',
|
633
|
+
'V', 'SizeOfHeapReserve',
|
634
|
+
'V', 'SizeOfHeapCommit',
|
635
|
+
'V', 'LoaderFlags',
|
636
|
+
'V', 'NumberOfRvaAndSizes',
|
637
|
+
])
|
638
|
+
SIZEOF_IMAGE_OPTIONAL_HEADER32 = IMAGE_OPTIONAL_HEADER32.size
|
639
|
+
|
640
|
+
IMAGE_OPTIONAL_HEADER64 = BinaryStruct.new([
|
641
|
+
'v', 'Magic',
|
642
|
+
'c', 'MajorLinkerVersion',
|
643
|
+
'c', 'MinorLinkerVersion',
|
644
|
+
'V', 'SizeOfCode',
|
645
|
+
'V', 'SizeOfInitializedData',
|
646
|
+
'V', 'SizeOfUninitializedData',
|
647
|
+
'V', 'AddressOfEntryPoint',
|
648
|
+
'V', 'BaseOfCode',
|
649
|
+
'Q', 'ImageBase',
|
650
|
+
'V', 'SectionAlignment',
|
651
|
+
'V', 'FileAlignment',
|
652
|
+
'v', 'MajorOperatingSystemVersion',
|
653
|
+
'v', 'MinorOperatingSystemVersion',
|
654
|
+
'v', 'MajorImageVersion',
|
655
|
+
'v', 'MinorImageVersion',
|
656
|
+
'v', 'MajorSubsystemVersion',
|
657
|
+
'v', 'MinorSubsystemVersion',
|
658
|
+
'V', 'Win32VersionValue',
|
659
|
+
'V', 'SizeOfImage',
|
660
|
+
'V', 'SizeOfHeaders',
|
661
|
+
'V', 'CheckSum',
|
662
|
+
'v', 'Subsystem',
|
663
|
+
'v', 'DllCharacteristics',
|
664
|
+
'Q', 'SizeOfStackReserve',
|
665
|
+
'Q', 'SizeOfStackCommit',
|
666
|
+
'Q', 'SizeOfHeapReserve',
|
667
|
+
'Q', 'SizeOfHeapCommit',
|
668
|
+
'V', 'LoaderFlags',
|
669
|
+
'V', 'NumberOfRvaAndSizes',
|
670
|
+
])
|
671
|
+
SIZEOF_IMAGE_OPTIONAL_HEADER64 = IMAGE_OPTIONAL_HEADER64.size
|
672
|
+
|
673
|
+
IMAGE_DATA_DIRECTORY = BinaryStruct.new([
|
674
|
+
'V', :virtualAddress,
|
675
|
+
'V', :size,
|
676
|
+
])
|
677
|
+
SIZEOF_IMAGE_DATA_DIRECTORY = IMAGE_DATA_DIRECTORY.size
|
678
|
+
|
679
|
+
IMAGE_SECTION_HEADER = BinaryStruct.new([
|
680
|
+
'a8', 'Name',
|
681
|
+
# union {
|
682
|
+
# DWORD PhysicalAddress;
|
683
|
+
# DWORD VirtualSize;
|
684
|
+
# } Misc;
|
685
|
+
'V', :VirtualSize,
|
686
|
+
'V', :virtualAddress,
|
687
|
+
'V', 'SizeOfRawData',
|
688
|
+
'V', :PointerToRawData,
|
689
|
+
'V', 'PointerToRelocations',
|
690
|
+
'V', 'PointerToLinenumbers',
|
691
|
+
'v', 'NumberOfRelocations',
|
692
|
+
'v', 'NumberOfLinenumbers',
|
693
|
+
'V', 'Characteristics',
|
694
|
+
])
|
695
|
+
SIZEOF_IMAGE_SECTION_HEADER = IMAGE_SECTION_HEADER.size
|
696
|
+
|
697
|
+
IMAGE_IMPORT_DESCRIPTOR = BinaryStruct.new([
|
698
|
+
# union {
|
699
|
+
# DWORD Characteristics; #// 0 for terminating null import descriptor
|
700
|
+
# DWORD OriginalFirstThunk; #// RVA to original unbound IAT (PIMAGE_THUNK_DATA)
|
701
|
+
# };
|
702
|
+
'V', 'Characteristics',
|
703
|
+
'V', 'TimeDateStamp', # // 0 if not bound,
|
704
|
+
# // -1 if bound, and real date\time stamp
|
705
|
+
# // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
|
706
|
+
# // O.W. date/time stamp of DLL bound to (Old BIND)
|
707
|
+
|
708
|
+
'V', 'ForwarderChain', # // -1 if no forwarders
|
709
|
+
'V', 'Name',
|
710
|
+
'V', 'FirstThunk', # // RVA to IAT (if bound this IAT has actual addresses)
|
711
|
+
])
|
712
|
+
SIZEOF_IMAGE_IMPORT_DESCRIPTOR = IMAGE_IMPORT_DESCRIPTOR.size
|
713
|
+
|
714
|
+
# General resource definitions.
|
715
|
+
IMAGE_RESOURCE_DIRECTORY = BinaryStruct.new([
|
716
|
+
'L', :characteristics,
|
717
|
+
'L', :timeDateStamp,
|
718
|
+
'S', :majorVersion,
|
719
|
+
'S', :minorVersion,
|
720
|
+
'S', :numberOfNamedEntries, # Number of named entries that follow this struc (first).
|
721
|
+
'S', :numberOfIdEntries, # Number of ID entries that follow this struc (second).
|
722
|
+
])
|
723
|
+
SIZEOF_IMAGE_RESOURCE_DIRECTORY = IMAGE_RESOURCE_DIRECTORY.size
|
724
|
+
|
725
|
+
IMAGE_RESOURCE_DIRECTORY_ENTRY = BinaryStruct.new([
|
726
|
+
'L', :name, # Name or ID. If bit 31 = 0 then ID. If bit 31 = 1, then
|
727
|
+
# bits 0-30 are an offset (from start of rsrc) to IMAGE_RESOURCE_DIR_STRING_U.
|
728
|
+
'L', :offsetToData, # Ptr to dir or data. If bit 31 = 0, then ptr to
|
729
|
+
# IMAGE_REDSOURCE_DATA_ENTRY. If bit 31 = 1, then bits 0-30 are ptr to IMAGE_RESOURCE_DIRECTORY.
|
730
|
+
])
|
731
|
+
SIZEOF_IMAGE_RESOURCE_DIRECTORY_ENTRY = IMAGE_RESOURCE_DIRECTORY_ENTRY.size
|
732
|
+
|
733
|
+
# NOTE: Skipping string resource name because it is self-referencing:
|
734
|
+
# typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
|
735
|
+
# WORD Length;
|
736
|
+
# WCHAR NameString[ 1 ];
|
737
|
+
# } IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;
|
738
|
+
# The member NameString is Length characters long, so the final size of the structure is unknown.
|
739
|
+
# This is just handled without BinaryStruct.
|
740
|
+
|
741
|
+
IMAGE_RESOURCE_DATA_ENTRY = BinaryStruct.new([
|
742
|
+
'L', :offsetToData, # This offset is an RVA.
|
743
|
+
'L', :size, # Size in bytes.
|
744
|
+
'L', :codePage, # Code page (for strings).
|
745
|
+
'L', :reserved1,
|
746
|
+
])
|
747
|
+
SIZEOF_IMAGE_RESOURCE_DATA_ENTRY = IMAGE_RESOURCE_DATA_ENTRY.size
|
748
|
+
|
749
|
+
# Icon specific resource definitions.
|
750
|
+
GRPICONDIR = BinaryStruct.new([
|
751
|
+
'S', :idReserved1,
|
752
|
+
'S', :idType, # 1 for icons.
|
753
|
+
'S', :idCount, # Count of images.
|
754
|
+
'a*', :data, # Array of GRPICONDIRENTRY.
|
755
|
+
])
|
756
|
+
SIZEOF_GRPICONDIR = GRPICONDIR.size # TODO: BinaryStruct.sizeof ignores the *
|
757
|
+
|
758
|
+
GRPICONDIRENTRY = BinaryStruct.new([
|
759
|
+
'C', :bWidth, # Pixel width of image.
|
760
|
+
'C', :bHeight, # Pixel height of image.
|
761
|
+
'C', :bColorCount, # Colors in image (0 if >= 8bpp).
|
762
|
+
'C', :bReserved1,
|
763
|
+
'S', :wPlanes, # Color planes.
|
764
|
+
'S', :wBitCount, # Bits per pixel.
|
765
|
+
'L', :dwBytesInRes, # Bytes in this resource.
|
766
|
+
'S', :nID, # Resource ID.
|
767
|
+
# NOTE: In an .ico file, last member is 'L', 'dwImageOffset', an offset
|
768
|
+
# from the beginning of the file to the BITMAPINFOHEADER of the icon data.
|
769
|
+
])
|
770
|
+
SIZEOF_GRPICONDIRENTRY = GRPICONDIRENTRY.size
|
771
|
+
|
772
|
+
# Messagetable specific resource definitions.
|
773
|
+
MESSAGE_RESOURCE_DATA = BinaryStruct.new([
|
774
|
+
'L', :numberOfBlocks, # Length of data array.
|
775
|
+
'a*', :data, # Array of MESSAGE_RESOURCE_BLOCK.
|
776
|
+
])
|
777
|
+
SIZEOF_MESSAGE_RESOURCE_DATA = MESSAGE_RESOURCE_DATA.size # TODO: BinaryStruct.sizeof ignores the *
|
778
|
+
|
779
|
+
MESSAGE_RESOURCE_BLOCK = BinaryStruct.new([
|
780
|
+
'L', :loId,
|
781
|
+
'L', :hiId,
|
782
|
+
'L', :offsetToEntries, # RVA?
|
783
|
+
])
|
784
|
+
SIZEOF_MESSAGE_RESOURCE_BLOCK = MESSAGE_RESOURCE_BLOCK.size
|
785
|
+
SIZEOF_MRB = 12
|
786
|
+
|
787
|
+
MESSAGE_RESOURCE_ENTRY = BinaryStruct.new([
|
788
|
+
'S', :length, # String length.
|
789
|
+
'S', :flags, # Encoding (see below).
|
790
|
+
])
|
791
|
+
SIZEOF_MRE = 4
|
792
|
+
SIZEOF_MESSAGE_RESOURCE_ENTRY = MESSAGE_RESOURCE_ENTRY.size
|
793
|
+
# Text follows here.
|
794
|
+
|
795
|
+
MESSAGE_RESOURCE_ANSI = 0x0000 # If set text is ANSI.
|
796
|
+
MESSAGE_RESOURCE_UNICODE = 0x0001 # If set text is UNICODE.
|
797
|
+
|
798
|
+
VS_VERSION_INFO_HEADER = BinaryStruct.new([
|
799
|
+
'a32', 'sig',
|
800
|
+
's', nil,
|
801
|
+
's', nil,
|
802
|
+
's', nil,
|
803
|
+
's', nil,
|
804
|
+
's', nil,
|
805
|
+
'S', 'fminor',
|
806
|
+
'S', 'fmajor',
|
807
|
+
'S', 'fbuild',
|
808
|
+
'S', 'frev',
|
809
|
+
'S', 'pminor',
|
810
|
+
'S', 'pmajor',
|
811
|
+
'S', 'pbuild',
|
812
|
+
'S', 'prev',
|
813
|
+
])
|
814
|
+
SIZEOF_VS_VERSION_INFO_HEADER = VS_VERSION_INFO_HEADER.size
|
815
|
+
|
816
|
+
STRING_INFO_HEADER = BinaryStruct.new([
|
817
|
+
'a30', 'sig',
|
818
|
+
'V', 'data_length',
|
819
|
+
's', 'type',
|
820
|
+
'a8', 'lang',
|
821
|
+
'a8', 'code_page',
|
822
|
+
])
|
823
|
+
SIZEOF_STRING_INFO_HEADER = STRING_INFO_HEADER.size
|
824
|
+
|
825
|
+
VERSION_STRING_HEADER = BinaryStruct.new([
|
826
|
+
's', 'zero',
|
827
|
+
's', 'slen',
|
828
|
+
's', 'vlen',
|
829
|
+
's', 'type',
|
830
|
+
])
|
831
|
+
SIZEOF_VERSION_STRING_HEADER = VERSION_STRING_HEADER.size
|
832
|
+
|
833
|
+
STRINGFILEINFO = "S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0"
|
834
|
+
VS_VERSION_INFO = "V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0\0\0"
|
835
|
+
|
836
|
+
IMAGE_SIZEOF_NT_OPTIONAL32_HEADER = 224
|
837
|
+
IMAGE_SIZEOF_NT_OPTIONAL64_HEADER = 240
|
838
|
+
|
839
|
+
IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
|
840
|
+
IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
|
841
|
+
|
842
|
+
private
|
843
|
+
|
844
|
+
def bit?(num, bitNum)
|
845
|
+
msk = 1 << bitNum
|
846
|
+
num & msk == msk
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
###########################################################
|
851
|
+
# Only run if we are calling this script directly
|
852
|
+
if __FILE__ == $0
|
853
|
+
st = Time.now
|
854
|
+
puts "Running script [#{__FILE__}]"
|
855
|
+
fileName = "D:/temp/icons/PSPad.exe"
|
856
|
+
fileName = "D:/temp/icons/EventMsg2.dll"
|
857
|
+
peHdr = PEheader.new(fileName)
|
858
|
+
puts "Imports:[#{peHdr.imports.length}] - #{peHdr.imports.join(", ")}"
|
859
|
+
puts "VerionsInfo: #{peHdr.versioninfo.inspect}"
|
860
|
+
puts "Icon Count: [#{peHdr.icons.length}]"
|
861
|
+
# Dump icons to d:\temp\icons\icon{n}.ico
|
862
|
+
peHdr.icons.each_with_index { |icon, ico| File.open("d:/temp/icons/icon#{ico}.ico", "wb") { |f| f.write(icon) } }
|
863
|
+
|
864
|
+
puts "MessageTable Count: [#{peHdr.messagetables.length}]"
|
865
|
+
peHdr.messagetables.each { |m| puts m }
|
866
|
+
|
867
|
+
puts "completed script [#{__FILE__}] [#{Time.now - st}]"
|
868
|
+
end
|