From 3ca1097ac39640db628e04d067317ffeeb87af83 Mon Sep 17 00:00:00 2001 From: rukaimi <rukaimi@985867f9-ca9c-e1f6-822d-e8a4186388af> Date: Wed, 7 Nov 2012 15:32:30 +0000 Subject: [PATCH] Version 1.0.0 commit. A HUGE PILE of bugs was fixed! Added unit-tests. The code has been refactored. Now it is separated into dozens of independent files. Added some functionality (bound import rebuilder etc) --- Makefile | 9 +- pe_bliss_tests_vc10.sln | 272 + pe_bliss_tests_vc9.sln | 272 + pe_lib_vc10.sln => pe_bliss_vc10.sln | 6 +- pe_lib_vc9.sln => pe_bliss_vc9.sln | 4 + pe_lib/Makefile | 2 +- pe_lib/entropy.cpp | 90 + pe_lib/entropy.h | 30 + pe_lib/file_version_info.cpp | 419 ++ pe_lib/file_version_info.h | 178 + pe_lib/message_table.cpp | 60 + pe_lib/message_table.h | 35 + pe_lib/pe_32_64.cpp | 2 +- pe_lib/pe_base.cpp | 5041 +++-------------- pe_lib/pe_base.h | 1938 +------ pe_lib/pe_bliss.h | 18 + pe_lib/pe_bliss_resources.h | 15 + pe_lib/pe_bound_import.cpp | 290 + pe_lib/pe_bound_import.h | 87 + pe_lib/pe_checksum.cpp | 82 + pe_lib/pe_checksum.h | 9 + pe_lib/pe_debug.cpp | 844 +++ pe_lib/pe_debug.h | 303 + pe_lib/pe_directory.cpp | 38 + pe_lib/pe_directory.h | 29 + pe_lib/pe_dotnet.cpp | 165 + pe_lib/pe_dotnet.h | 76 + pe_lib/pe_exception.h | 10 +- pe_lib/pe_exception_directory.cpp | 156 + pe_lib/pe_exception_directory.h | 67 + pe_lib/pe_exports.cpp | 679 +++ pe_lib/pe_exports.h | 163 + pe_lib/pe_factory.cpp | 16 +- pe_lib/pe_factory.h | 2 +- pe_lib/pe_imports.cpp | 756 +++ pe_lib/pe_imports.h | 187 + pe_lib/pe_lib.vcproj | 350 +- pe_lib/pe_lib.vcxproj | 76 +- pe_lib/pe_lib.vcxproj.filters | 238 +- pe_lib/pe_load_config.cpp | 536 ++ pe_lib/pe_load_config.h | 163 + pe_lib/pe_properties.cpp | 20 + pe_lib/pe_properties.h | 215 + pe_lib/pe_properties_generic.cpp | 624 ++ pe_lib/pe_properties_generic.h | 256 + pe_lib/pe_rebuilder.cpp | 177 + pe_lib/pe_rebuilder.h | 11 + pe_lib/pe_relocations.cpp | 299 + pe_lib/pe_relocations.h | 101 + pe_lib/pe_resource_manager.cpp | 2943 +--------- pe_lib/pe_resource_manager.h | 647 +-- pe_lib/pe_resource_viewer.cpp | 361 ++ pe_lib/pe_resource_viewer.h | 132 + pe_lib/pe_resources.cpp | 705 +++ pe_lib/pe_resources.h | 224 + pe_lib/pe_rich_data.cpp | 131 + pe_lib/pe_rich_data.h | 37 + pe_lib/pe_section.cpp | 281 + pe_lib/pe_section.h | 137 + pe_lib/pe_structures.h | 9 +- pe_lib/pe_tls.cpp | 375 ++ pe_lib/pe_tls.h | 101 + pe_lib/readme.txt | 4 +- pe_lib/resource_bitmap_reader.cpp | 65 + pe_lib/resource_bitmap_reader.h | 29 + pe_lib/resource_bitmap_writer.cpp | 54 + pe_lib/resource_bitmap_writer.h | 26 + pe_lib/resource_cursor_icon_reader.cpp | 500 ++ pe_lib/resource_cursor_icon_reader.h | 63 + pe_lib/resource_cursor_icon_writer.cpp | 426 ++ pe_lib/resource_cursor_icon_writer.h | 73 + pe_lib/resource_data_info.cpp | 27 + pe_lib/resource_data_info.h | 27 + pe_lib/resource_internal.h | 13 + pe_lib/resource_message_list_reader.cpp | 110 + pe_lib/resource_message_list_reader.h | 28 + pe_lib/resource_string_table_reader.cpp | 88 + pe_lib/resource_string_table_reader.h | 36 + pe_lib/resource_version_info_reader.cpp | 290 + pe_lib/resource_version_info_reader.h | 46 + pe_lib/resource_version_info_writer.cpp | 262 + pe_lib/resource_version_info_writer.h | 31 + pe_lib/stdint_defs.h | 2 + pe_lib/utils.cpp | 86 + pe_lib/utils.h | 84 + pe_lib/version_info_editor.cpp | 163 + pe_lib/version_info_editor.h | 58 + pe_lib/version_info_types.h | 17 + pe_lib/version_info_viewer.cpp | 159 + pe_lib/version_info_viewer.h | 68 + .../address_convertions.vcproj | 50 +- .../address_convertions.vcxproj | 4 +- samples/address_convertions/main.cpp | 14 +- .../basic_dotnet_viewer.vcproj | 50 +- .../basic_dotnet_viewer.vcxproj | 4 +- samples/basic_dotnet_viewer/main.cpp | 8 +- .../basic_info_viewer.vcproj | 50 +- .../basic_info_viewer.vcxproj | 4 +- samples/basic_info_viewer/main.cpp | 50 +- .../bound_import_reader.vcproj | 50 +- .../bound_import_reader.vcxproj | 4 +- samples/bound_import_reader/main.cpp | 16 +- .../debug_info_reader.vcproj | 50 +- .../debug_info_reader.vcxproj | 4 +- samples/debug_info_reader/main.cpp | 58 +- .../entropy_calculator.vcproj | 50 +- .../entropy_calculator.vcxproj | 4 +- samples/entropy_calculator/main.cpp | 14 +- .../exception_dir_reader.vcproj | 50 +- .../exception_dir_reader.vcxproj | 4 +- samples/exception_dir_reader/main.cpp | 12 +- samples/export_adder/export_adder.vcproj | 4 +- samples/export_adder/export_adder.vcxproj | 4 +- samples/export_adder/main.cpp | 22 +- samples/exports_reader/exports_reader.vcproj | 50 +- samples/exports_reader/exports_reader.vcxproj | 4 +- samples/exports_reader/main.cpp | 14 +- .../full_pe_rebuilder.vcproj | 50 +- .../full_pe_rebuilder.vcxproj | 4 +- samples/full_pe_rebuilder/main.cpp | 51 +- .../image_config_editor.vcproj | 50 +- .../image_config_editor.vcxproj | 4 +- samples/image_config_editor/main.cpp | 14 +- samples/import_adder/import_adder.vcproj | 50 +- samples/import_adder/import_adder.vcxproj | 4 +- samples/import_adder/main.cpp | 22 +- samples/imports_reader/imports_reader.vcproj | 50 +- samples/imports_reader/imports_reader.vcxproj | 4 +- samples/imports_reader/main.cpp | 18 +- samples/pe_config_reader/main.cpp | 10 +- .../pe_config_reader/pe_config_reader.vcproj | 50 +- .../pe_config_reader/pe_config_reader.vcxproj | 4 +- samples/pe_realigner/main.cpp | 10 +- samples/pe_realigner/pe_realigner.vcproj | 50 +- samples/pe_realigner/pe_realigner.vcxproj | 4 +- samples/pe_rebaser/main.cpp | 15 +- samples/pe_rebaser/pe_rebaser.vcproj | 50 +- samples/pe_rebaser/pe_rebaser.vcxproj | 4 +- samples/pe_sections_reader/main.cpp | 10 +- .../pe_sections_reader.vcproj | 50 +- .../pe_sections_reader.vcxproj | 4 +- samples/pe_stripper/main.cpp | 12 +- samples/pe_stripper/pe_stripper.vcproj | 50 +- samples/pe_stripper/pe_stripper.vcxproj | 4 +- samples/relocation_adder/main.cpp | 18 +- .../relocation_adder/relocation_adder.vcproj | 50 +- .../relocation_adder/relocation_adder.vcxproj | 4 +- samples/relocations_reader/main.cpp | 16 +- .../relocations_reader.vcproj | 50 +- .../relocations_reader.vcxproj | 4 +- samples/resource_editor/main.cpp | 38 +- .../resource_editor/resource_editor.vcproj | 50 +- .../resource_editor/resource_editor.vcxproj | 4 +- samples/resource_viewer/main.cpp | 32 +- .../resource_viewer/resource_viewer.vcproj | 50 +- .../resource_viewer/resource_viewer.vcxproj | 4 +- samples/rich_overlay_stub_reader/main.cpp | 12 +- .../rich_overlay_stub_reader.vcproj | 50 +- .../rich_overlay_stub_reader.vcxproj | 4 +- samples/section_adder/main.cpp | 12 +- samples/section_adder/section_adder.vcproj | 50 +- samples/section_adder/section_adder.vcxproj | 4 +- samples/sections_and_addresses/main.cpp | 12 +- .../sections_and_addresses.vcproj | 50 +- .../sections_and_addresses.vcxproj | 4 +- samples/tls_editor/main.cpp | 16 +- samples/tls_editor/tls_editor.vcproj | 50 +- samples/tls_editor/tls_editor.vcxproj | 4 +- samples/tls_reader/main.cpp | 10 +- samples/tls_reader/tls_reader.vcproj | 50 +- samples/tls_reader/tls_reader.vcxproj | 4 +- tests/Makefile | 21 + tests/lib.h | 14 + tests/pe_files/TestApp.exe | Bin 0 -> 4608 bytes tests/pe_files/bound32.exe | Bin 0 -> 243712 bytes tests/pe_files/bound64.exe | Bin 0 -> 265728 bytes tests/pe_files/debug_test.exe | Bin 0 -> 114220 bytes tests/pe_files/image32.exe | Bin 0 -> 243712 bytes tests/pe_files/image64.exe | Bin 0 -> 265728 bytes tests/pe_files/message_table_resource.exe | Bin 0 -> 6656 bytes tests/pe_files/test_dll_32.dll | Bin 0 -> 6656 bytes tests/pe_files/test_dll_64.dll | Bin 0 -> 7680 bytes tests/test.h | 124 + tests/test_bound_import/Makefile | 1 + tests/test_bound_import/main.cpp | 83 + .../test_bound_import.vcproj | 359 ++ .../test_bound_import.vcxproj | 163 + .../test_bound_import.vcxproj.filters | 22 + tests/test_checksum/Makefile | 1 + tests/test_checksum/main.cpp | 28 + tests/test_checksum/test_checksum.vcproj | 359 ++ tests/test_checksum/test_checksum.vcxproj | 168 + .../test_checksum.vcxproj.filters | 30 + tests/test_debug/Makefile | 1 + tests/test_debug/main.cpp | 122 + tests/test_debug/test_debug.vcproj | 359 ++ tests/test_debug/test_debug.vcxproj | 167 + tests/test_debug/test_debug.vcxproj.filters | 30 + tests/test_dotnet/Makefile | 1 + tests/test_dotnet/main.cpp | 39 + tests/test_dotnet/test_dotnet.vcproj | 359 ++ tests/test_dotnet/test_dotnet.vcxproj | 168 + tests/test_dotnet/test_dotnet.vcxproj.filters | 30 + tests/test_entropy/Makefile | 1 + tests/test_entropy/main.cpp | 34 + tests/test_entropy/test_entropy.vcproj | 359 ++ tests/test_entropy/test_entropy.vcxproj | 167 + .../test_entropy/test_entropy.vcxproj.filters | 30 + tests/test_exception_directory/Makefile | 1 + tests/test_exception_directory/main.cpp | 40 + .../test_exception_directory.vcproj | 359 ++ .../test_exception_directory.vcxproj | 167 + .../test_exception_directory.vcxproj.filters | 30 + tests/test_exports/Makefile | 1 + tests/test_exports/main.cpp | 147 + tests/test_exports/test_exports.vcproj | 359 ++ tests/test_exports/test_exports.vcxproj | 167 + .../test_exports/test_exports.vcxproj.filters | 30 + tests/test_imports/Makefile | 1 + tests/test_imports/main.cpp | 192 + tests/test_imports/test_imports.vcproj | 359 ++ tests/test_imports/test_imports.vcxproj | 167 + .../test_imports/test_imports.vcxproj.filters | 30 + tests/test_load_config/Makefile | 1 + tests/test_load_config/main.cpp | 83 + .../test_load_config/test_load_config.vcproj | 359 ++ .../test_load_config/test_load_config.vcxproj | 167 + .../test_load_config.vcxproj.filters | 30 + tests/test_relocations/Makefile | 1 + tests/test_relocations/main.cpp | 100 + .../test_relocations/test_relocations.vcproj | 359 ++ .../test_relocations/test_relocations.vcxproj | 167 + .../test_relocations.vcxproj.filters | 30 + tests/test_resource_bitmap/Makefile | 1 + tests/test_resource_bitmap/main.cpp | 59 + .../test_resource_bitmap.vcproj | 359 ++ .../test_resource_bitmap.vcxproj | 168 + .../test_resource_bitmap.vcxproj.filters | 30 + tests/test_resource_icon_cursor/Makefile | 1 + tests/test_resource_icon_cursor/main.cpp | 120 + .../test_resource_icon_cursor.vcproj | 359 ++ .../test_resource_icon_cursor.vcxproj | 167 + .../test_resource_icon_cursor.vcxproj.filters | 30 + tests/test_resource_manager/Makefile | 1 + tests/test_resource_manager/main.cpp | 68 + .../test_resource_manager.vcproj | 359 ++ .../test_resource_manager.vcxproj | 167 + .../test_resource_manager.vcxproj.filters | 30 + tests/test_resource_message_table/Makefile | 1 + tests/test_resource_message_table/main.cpp | 62 + .../test_resource_message_table.vcproj | 359 ++ .../test_resource_message_table.vcxproj | 167 + ...est_resource_message_table.vcxproj.filters | 30 + tests/test_resource_string_table/Makefile | 1 + tests/test_resource_string_table/main.cpp | 41 + .../test_resource_string_table.vcproj | 359 ++ .../test_resource_string_table.vcxproj | 167 + ...test_resource_string_table.vcxproj.filters | 30 + tests/test_resource_version_info/Makefile | 1 + tests/test_resource_version_info/main.cpp | 97 + .../test_resource_version_info.vcproj | 359 ++ .../test_resource_version_info.vcxproj | 167 + ...test_resource_version_info.vcxproj.filters | 30 + tests/test_resource_viewer/Makefile | 1 + tests/test_resource_viewer/main.cpp | 89 + .../test_resource_viewer.vcproj | 359 ++ .../test_resource_viewer.vcxproj | 167 + .../test_resource_viewer.vcxproj.filters | 30 + tests/test_resources/Makefile | 1 + tests/test_resources/main.cpp | 89 + tests/test_resources/test_resources.vcproj | 359 ++ tests/test_resources/test_resources.vcxproj | 167 + .../test_resources.vcxproj.filters | 30 + tests/test_rich_data/Makefile | 1 + tests/test_rich_data/main.cpp | 40 + tests/test_rich_data/test_rich_data.vcproj | 359 ++ tests/test_rich_data/test_rich_data.vcxproj | 167 + .../test_rich_data.vcxproj.filters | 30 + tests/test_runner/Makefile | 1 + tests/test_runner/main.cpp | 212 + tests/test_runner/test_runner.vcproj | 351 ++ tests/test_runner/test_runner.vcxproj | 163 + tests/test_runner/test_runner.vcxproj.filters | 22 + tests/test_tls/Makefile | 1 + tests/test_tls/main.cpp | 101 + tests/test_tls/test_tls.vcproj | 359 ++ tests/test_tls/test_tls.vcxproj | 167 + tests/test_tls/test_tls.vcxproj.filters | 30 + tests/tests.mak | 23 + tests/tests_basic/Makefile | 1 + tests/tests_basic/main.cpp | 496 ++ tests/tests_basic/tests_basic.vcproj | 359 ++ tests/tests_basic/tests_basic.vcxproj | 167 + tests/tests_basic/tests_basic.vcxproj.filters | 30 + tests/tests_utils/Makefile | 1 + tests/tests_utils/main.cpp | 52 + tests/tests_utils/tests_utils.vcproj | 359 ++ tests/tests_utils/tests_utils.vcxproj | 167 + tests/tests_utils/tests_utils.vcxproj.filters | 30 + 299 files changed, 31011 insertions(+), 10629 deletions(-) create mode 100644 pe_bliss_tests_vc10.sln create mode 100644 pe_bliss_tests_vc9.sln rename pe_lib_vc10.sln => pe_bliss_vc10.sln (97%) rename pe_lib_vc9.sln => pe_bliss_vc9.sln (97%) create mode 100644 pe_lib/entropy.cpp create mode 100644 pe_lib/entropy.h create mode 100644 pe_lib/file_version_info.cpp create mode 100644 pe_lib/file_version_info.h create mode 100644 pe_lib/message_table.cpp create mode 100644 pe_lib/message_table.h create mode 100644 pe_lib/pe_bliss.h create mode 100644 pe_lib/pe_bliss_resources.h create mode 100644 pe_lib/pe_bound_import.cpp create mode 100644 pe_lib/pe_bound_import.h create mode 100644 pe_lib/pe_checksum.cpp create mode 100644 pe_lib/pe_checksum.h create mode 100644 pe_lib/pe_debug.cpp create mode 100644 pe_lib/pe_debug.h create mode 100644 pe_lib/pe_directory.cpp create mode 100644 pe_lib/pe_directory.h create mode 100644 pe_lib/pe_dotnet.cpp create mode 100644 pe_lib/pe_dotnet.h create mode 100644 pe_lib/pe_exception_directory.cpp create mode 100644 pe_lib/pe_exception_directory.h create mode 100644 pe_lib/pe_exports.cpp create mode 100644 pe_lib/pe_exports.h create mode 100644 pe_lib/pe_imports.cpp create mode 100644 pe_lib/pe_imports.h create mode 100644 pe_lib/pe_load_config.cpp create mode 100644 pe_lib/pe_load_config.h create mode 100644 pe_lib/pe_properties.cpp create mode 100644 pe_lib/pe_properties.h create mode 100644 pe_lib/pe_properties_generic.cpp create mode 100644 pe_lib/pe_properties_generic.h create mode 100644 pe_lib/pe_rebuilder.cpp create mode 100644 pe_lib/pe_rebuilder.h create mode 100644 pe_lib/pe_relocations.cpp create mode 100644 pe_lib/pe_relocations.h create mode 100644 pe_lib/pe_resource_viewer.cpp create mode 100644 pe_lib/pe_resource_viewer.h create mode 100644 pe_lib/pe_resources.cpp create mode 100644 pe_lib/pe_resources.h create mode 100644 pe_lib/pe_rich_data.cpp create mode 100644 pe_lib/pe_rich_data.h create mode 100644 pe_lib/pe_section.cpp create mode 100644 pe_lib/pe_section.h create mode 100644 pe_lib/pe_tls.cpp create mode 100644 pe_lib/pe_tls.h create mode 100644 pe_lib/resource_bitmap_reader.cpp create mode 100644 pe_lib/resource_bitmap_reader.h create mode 100644 pe_lib/resource_bitmap_writer.cpp create mode 100644 pe_lib/resource_bitmap_writer.h create mode 100644 pe_lib/resource_cursor_icon_reader.cpp create mode 100644 pe_lib/resource_cursor_icon_reader.h create mode 100644 pe_lib/resource_cursor_icon_writer.cpp create mode 100644 pe_lib/resource_cursor_icon_writer.h create mode 100644 pe_lib/resource_data_info.cpp create mode 100644 pe_lib/resource_data_info.h create mode 100644 pe_lib/resource_internal.h create mode 100644 pe_lib/resource_message_list_reader.cpp create mode 100644 pe_lib/resource_message_list_reader.h create mode 100644 pe_lib/resource_string_table_reader.cpp create mode 100644 pe_lib/resource_string_table_reader.h create mode 100644 pe_lib/resource_version_info_reader.cpp create mode 100644 pe_lib/resource_version_info_reader.h create mode 100644 pe_lib/resource_version_info_writer.cpp create mode 100644 pe_lib/resource_version_info_writer.h create mode 100644 pe_lib/utils.cpp create mode 100644 pe_lib/utils.h create mode 100644 pe_lib/version_info_editor.cpp create mode 100644 pe_lib/version_info_editor.h create mode 100644 pe_lib/version_info_types.h create mode 100644 pe_lib/version_info_viewer.cpp create mode 100644 pe_lib/version_info_viewer.h create mode 100644 tests/Makefile create mode 100644 tests/lib.h create mode 100644 tests/pe_files/TestApp.exe create mode 100644 tests/pe_files/bound32.exe create mode 100644 tests/pe_files/bound64.exe create mode 100644 tests/pe_files/debug_test.exe create mode 100644 tests/pe_files/image32.exe create mode 100644 tests/pe_files/image64.exe create mode 100644 tests/pe_files/message_table_resource.exe create mode 100644 tests/pe_files/test_dll_32.dll create mode 100644 tests/pe_files/test_dll_64.dll create mode 100644 tests/test.h create mode 100644 tests/test_bound_import/Makefile create mode 100644 tests/test_bound_import/main.cpp create mode 100644 tests/test_bound_import/test_bound_import.vcproj create mode 100644 tests/test_bound_import/test_bound_import.vcxproj create mode 100644 tests/test_bound_import/test_bound_import.vcxproj.filters create mode 100644 tests/test_checksum/Makefile create mode 100644 tests/test_checksum/main.cpp create mode 100644 tests/test_checksum/test_checksum.vcproj create mode 100644 tests/test_checksum/test_checksum.vcxproj create mode 100644 tests/test_checksum/test_checksum.vcxproj.filters create mode 100644 tests/test_debug/Makefile create mode 100644 tests/test_debug/main.cpp create mode 100644 tests/test_debug/test_debug.vcproj create mode 100644 tests/test_debug/test_debug.vcxproj create mode 100644 tests/test_debug/test_debug.vcxproj.filters create mode 100644 tests/test_dotnet/Makefile create mode 100644 tests/test_dotnet/main.cpp create mode 100644 tests/test_dotnet/test_dotnet.vcproj create mode 100644 tests/test_dotnet/test_dotnet.vcxproj create mode 100644 tests/test_dotnet/test_dotnet.vcxproj.filters create mode 100644 tests/test_entropy/Makefile create mode 100644 tests/test_entropy/main.cpp create mode 100644 tests/test_entropy/test_entropy.vcproj create mode 100644 tests/test_entropy/test_entropy.vcxproj create mode 100644 tests/test_entropy/test_entropy.vcxproj.filters create mode 100644 tests/test_exception_directory/Makefile create mode 100644 tests/test_exception_directory/main.cpp create mode 100644 tests/test_exception_directory/test_exception_directory.vcproj create mode 100644 tests/test_exception_directory/test_exception_directory.vcxproj create mode 100644 tests/test_exception_directory/test_exception_directory.vcxproj.filters create mode 100644 tests/test_exports/Makefile create mode 100644 tests/test_exports/main.cpp create mode 100644 tests/test_exports/test_exports.vcproj create mode 100644 tests/test_exports/test_exports.vcxproj create mode 100644 tests/test_exports/test_exports.vcxproj.filters create mode 100644 tests/test_imports/Makefile create mode 100644 tests/test_imports/main.cpp create mode 100644 tests/test_imports/test_imports.vcproj create mode 100644 tests/test_imports/test_imports.vcxproj create mode 100644 tests/test_imports/test_imports.vcxproj.filters create mode 100644 tests/test_load_config/Makefile create mode 100644 tests/test_load_config/main.cpp create mode 100644 tests/test_load_config/test_load_config.vcproj create mode 100644 tests/test_load_config/test_load_config.vcxproj create mode 100644 tests/test_load_config/test_load_config.vcxproj.filters create mode 100644 tests/test_relocations/Makefile create mode 100644 tests/test_relocations/main.cpp create mode 100644 tests/test_relocations/test_relocations.vcproj create mode 100644 tests/test_relocations/test_relocations.vcxproj create mode 100644 tests/test_relocations/test_relocations.vcxproj.filters create mode 100644 tests/test_resource_bitmap/Makefile create mode 100644 tests/test_resource_bitmap/main.cpp create mode 100644 tests/test_resource_bitmap/test_resource_bitmap.vcproj create mode 100644 tests/test_resource_bitmap/test_resource_bitmap.vcxproj create mode 100644 tests/test_resource_bitmap/test_resource_bitmap.vcxproj.filters create mode 100644 tests/test_resource_icon_cursor/Makefile create mode 100644 tests/test_resource_icon_cursor/main.cpp create mode 100644 tests/test_resource_icon_cursor/test_resource_icon_cursor.vcproj create mode 100644 tests/test_resource_icon_cursor/test_resource_icon_cursor.vcxproj create mode 100644 tests/test_resource_icon_cursor/test_resource_icon_cursor.vcxproj.filters create mode 100644 tests/test_resource_manager/Makefile create mode 100644 tests/test_resource_manager/main.cpp create mode 100644 tests/test_resource_manager/test_resource_manager.vcproj create mode 100644 tests/test_resource_manager/test_resource_manager.vcxproj create mode 100644 tests/test_resource_manager/test_resource_manager.vcxproj.filters create mode 100644 tests/test_resource_message_table/Makefile create mode 100644 tests/test_resource_message_table/main.cpp create mode 100644 tests/test_resource_message_table/test_resource_message_table.vcproj create mode 100644 tests/test_resource_message_table/test_resource_message_table.vcxproj create mode 100644 tests/test_resource_message_table/test_resource_message_table.vcxproj.filters create mode 100644 tests/test_resource_string_table/Makefile create mode 100644 tests/test_resource_string_table/main.cpp create mode 100644 tests/test_resource_string_table/test_resource_string_table.vcproj create mode 100644 tests/test_resource_string_table/test_resource_string_table.vcxproj create mode 100644 tests/test_resource_string_table/test_resource_string_table.vcxproj.filters create mode 100644 tests/test_resource_version_info/Makefile create mode 100644 tests/test_resource_version_info/main.cpp create mode 100644 tests/test_resource_version_info/test_resource_version_info.vcproj create mode 100644 tests/test_resource_version_info/test_resource_version_info.vcxproj create mode 100644 tests/test_resource_version_info/test_resource_version_info.vcxproj.filters create mode 100644 tests/test_resource_viewer/Makefile create mode 100644 tests/test_resource_viewer/main.cpp create mode 100644 tests/test_resource_viewer/test_resource_viewer.vcproj create mode 100644 tests/test_resource_viewer/test_resource_viewer.vcxproj create mode 100644 tests/test_resource_viewer/test_resource_viewer.vcxproj.filters create mode 100644 tests/test_resources/Makefile create mode 100644 tests/test_resources/main.cpp create mode 100644 tests/test_resources/test_resources.vcproj create mode 100644 tests/test_resources/test_resources.vcxproj create mode 100644 tests/test_resources/test_resources.vcxproj.filters create mode 100644 tests/test_rich_data/Makefile create mode 100644 tests/test_rich_data/main.cpp create mode 100644 tests/test_rich_data/test_rich_data.vcproj create mode 100644 tests/test_rich_data/test_rich_data.vcxproj create mode 100644 tests/test_rich_data/test_rich_data.vcxproj.filters create mode 100644 tests/test_runner/Makefile create mode 100644 tests/test_runner/main.cpp create mode 100644 tests/test_runner/test_runner.vcproj create mode 100644 tests/test_runner/test_runner.vcxproj create mode 100644 tests/test_runner/test_runner.vcxproj.filters create mode 100644 tests/test_tls/Makefile create mode 100644 tests/test_tls/main.cpp create mode 100644 tests/test_tls/test_tls.vcproj create mode 100644 tests/test_tls/test_tls.vcxproj create mode 100644 tests/test_tls/test_tls.vcxproj.filters create mode 100644 tests/tests.mak create mode 100644 tests/tests_basic/Makefile create mode 100644 tests/tests_basic/main.cpp create mode 100644 tests/tests_basic/tests_basic.vcproj create mode 100644 tests/tests_basic/tests_basic.vcxproj create mode 100644 tests/tests_basic/tests_basic.vcxproj.filters create mode 100644 tests/tests_utils/Makefile create mode 100644 tests/tests_utils/main.cpp create mode 100644 tests/tests_utils/tests_utils.vcproj create mode 100644 tests/tests_utils/tests_utils.vcxproj create mode 100644 tests/tests_utils/tests_utils.vcxproj.filters diff --git a/Makefile b/Makefile index 3978329..775475e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -TARGETS = pe_bliss samples_pack -TARGETS_CLEAN = pe_clean samples_clean +TARGETS = pe_bliss samples_pack tests_pack +TARGETS_CLEAN = pe_clean samples_clean tests_clean all: $(TARGETS) @@ -17,3 +17,8 @@ pe_clean: samples_clean: $(MAKE) -C ./samples clean +tests_pack: pe_bliss + $(MAKE) PE_DEBUG=$(PE_DEBUG) -C ./tests + +tests_clean: + $(MAKE) -C ./tests clean diff --git a/pe_bliss_tests_vc10.sln b/pe_bliss_tests_vc10.sln new file mode 100644 index 0000000..975b954 --- /dev/null +++ b/pe_bliss_tests_vc10.sln @@ -0,0 +1,272 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_checksum", "tests\test_checksum\test_checksum.vcxproj", "{7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_entropy", "tests\test_entropy\test_entropy.vcxproj", "{853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_rich_data", "tests\test_rich_data\test_rich_data.vcxproj", "{114AC59B-BC28-40DB-8380-67C422D0C81B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests_basic", "tests\tests_basic\tests_basic.vcxproj", "{7870A9AC-92BB-423B-BC03-FBF7B46CD338}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests_utils", "tests\tests_utils\tests_utils.vcxproj", "{50212477-1614-49C9-9791-4AC72025DC76}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_runner", "tests\test_runner\test_runner.vcxproj", "{132DFCC9-13EF-4178-9772-1C467FB296D6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_imports", "tests\test_imports\test_imports.vcxproj", "{CE1D0620-BC75-456F-914B-3BEBF5444B4C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_relocations", "tests\test_relocations\test_relocations.vcxproj", "{709B0E41-9792-4A0A-B28B-CBD06CE441B9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_load_config", "tests\test_load_config\test_load_config.vcxproj", "{FAD361E1-1FD7-4993-BD20-7450026E51CC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_exception_directory", "tests\test_exception_directory\test_exception_directory.vcxproj", "{B6A37BAA-484D-4175-BEA2-62892A12E8F5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_tls", "tests\test_tls\test_tls.vcxproj", "{CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resources", "tests\test_resources\test_resources.vcxproj", "{8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_dotnet", "tests\test_dotnet\test_dotnet.vcxproj", "{F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_debug", "tests\test_debug\test_debug.vcxproj", "{B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_exports", "tests\test_exports\test_exports.vcxproj", "{82EAF17E-9618-4BD7-AE50-0C325591B585}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_bound_import", "tests\test_bound_import\test_bound_import.vcxproj", "{DA8A8F03-E719-45EF-A376-766A18772FA5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_viewer", "tests\test_resource_viewer\test_resource_viewer.vcxproj", "{1FC3537C-EC13-4877-A06C-42DD8B81CBF3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_manager", "tests\test_resource_manager\test_resource_manager.vcxproj", "{415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_bitmap", "tests\test_resource_bitmap\test_resource_bitmap.vcxproj", "{F401B9A2-B8CB-477A-A515-F029D0AA5553}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_icon_cursor", "tests\test_resource_icon_cursor\test_resource_icon_cursor.vcxproj", "{D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{6712270F-F056-4512-883A-1756A25D90E1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_string_table", "tests\test_resource_string_table\test_resource_string_table.vcxproj", "{5E32A144-2F2D-4BB1-BBEF-13BE94414E99}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_message_table", "tests\test_resource_message_table\test_resource_message_table.vcxproj", "{6CBACE55-8DDC-4EAE-A23A-DF412265D30C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_version_info", "tests\test_resource_version_info\test_resource_version_info.vcxproj", "{5C2B081E-5414-437B-86EB-B2695AEDF3F0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}.Debug|Win32.ActiveCfg = Debug|Win32 + {7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}.Debug|Win32.Build.0 = Debug|Win32 + {7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}.Debug|x64.ActiveCfg = Debug|x64 + {7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}.Debug|x64.Build.0 = Debug|x64 + {7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}.Release|Win32.ActiveCfg = Release|Win32 + {7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}.Release|Win32.Build.0 = Release|Win32 + {7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}.Release|x64.ActiveCfg = Release|x64 + {7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}.Release|x64.Build.0 = Release|x64 + {853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}.Debug|Win32.ActiveCfg = Debug|Win32 + {853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}.Debug|Win32.Build.0 = Debug|Win32 + {853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}.Debug|x64.ActiveCfg = Debug|x64 + {853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}.Debug|x64.Build.0 = Debug|x64 + {853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}.Release|Win32.ActiveCfg = Release|Win32 + {853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}.Release|Win32.Build.0 = Release|Win32 + {853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}.Release|x64.ActiveCfg = Release|x64 + {853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}.Release|x64.Build.0 = Release|x64 + {114AC59B-BC28-40DB-8380-67C422D0C81B}.Debug|Win32.ActiveCfg = Debug|Win32 + {114AC59B-BC28-40DB-8380-67C422D0C81B}.Debug|Win32.Build.0 = Debug|Win32 + {114AC59B-BC28-40DB-8380-67C422D0C81B}.Debug|x64.ActiveCfg = Debug|x64 + {114AC59B-BC28-40DB-8380-67C422D0C81B}.Debug|x64.Build.0 = Debug|x64 + {114AC59B-BC28-40DB-8380-67C422D0C81B}.Release|Win32.ActiveCfg = Release|Win32 + {114AC59B-BC28-40DB-8380-67C422D0C81B}.Release|Win32.Build.0 = Release|Win32 + {114AC59B-BC28-40DB-8380-67C422D0C81B}.Release|x64.ActiveCfg = Release|x64 + {114AC59B-BC28-40DB-8380-67C422D0C81B}.Release|x64.Build.0 = Release|x64 + {7870A9AC-92BB-423B-BC03-FBF7B46CD338}.Debug|Win32.ActiveCfg = Debug|Win32 + {7870A9AC-92BB-423B-BC03-FBF7B46CD338}.Debug|Win32.Build.0 = Debug|Win32 + {7870A9AC-92BB-423B-BC03-FBF7B46CD338}.Debug|x64.ActiveCfg = Debug|x64 + {7870A9AC-92BB-423B-BC03-FBF7B46CD338}.Debug|x64.Build.0 = Debug|x64 + {7870A9AC-92BB-423B-BC03-FBF7B46CD338}.Release|Win32.ActiveCfg = Release|Win32 + {7870A9AC-92BB-423B-BC03-FBF7B46CD338}.Release|Win32.Build.0 = Release|Win32 + {7870A9AC-92BB-423B-BC03-FBF7B46CD338}.Release|x64.ActiveCfg = Release|x64 + {7870A9AC-92BB-423B-BC03-FBF7B46CD338}.Release|x64.Build.0 = Release|x64 + {50212477-1614-49C9-9791-4AC72025DC76}.Debug|Win32.ActiveCfg = Debug|Win32 + {50212477-1614-49C9-9791-4AC72025DC76}.Debug|Win32.Build.0 = Debug|Win32 + {50212477-1614-49C9-9791-4AC72025DC76}.Debug|x64.ActiveCfg = Debug|x64 + {50212477-1614-49C9-9791-4AC72025DC76}.Debug|x64.Build.0 = Debug|x64 + {50212477-1614-49C9-9791-4AC72025DC76}.Release|Win32.ActiveCfg = Release|Win32 + {50212477-1614-49C9-9791-4AC72025DC76}.Release|Win32.Build.0 = Release|Win32 + {50212477-1614-49C9-9791-4AC72025DC76}.Release|x64.ActiveCfg = Release|x64 + {50212477-1614-49C9-9791-4AC72025DC76}.Release|x64.Build.0 = Release|x64 + {132DFCC9-13EF-4178-9772-1C467FB296D6}.Debug|Win32.ActiveCfg = Debug|Win32 + {132DFCC9-13EF-4178-9772-1C467FB296D6}.Debug|Win32.Build.0 = Debug|Win32 + {132DFCC9-13EF-4178-9772-1C467FB296D6}.Debug|x64.ActiveCfg = Debug|x64 + {132DFCC9-13EF-4178-9772-1C467FB296D6}.Debug|x64.Build.0 = Debug|x64 + {132DFCC9-13EF-4178-9772-1C467FB296D6}.Release|Win32.ActiveCfg = Release|Win32 + {132DFCC9-13EF-4178-9772-1C467FB296D6}.Release|Win32.Build.0 = Release|Win32 + {132DFCC9-13EF-4178-9772-1C467FB296D6}.Release|x64.ActiveCfg = Release|x64 + {132DFCC9-13EF-4178-9772-1C467FB296D6}.Release|x64.Build.0 = Release|x64 + {CE1D0620-BC75-456F-914B-3BEBF5444B4C}.Debug|Win32.ActiveCfg = Debug|Win32 + {CE1D0620-BC75-456F-914B-3BEBF5444B4C}.Debug|Win32.Build.0 = Debug|Win32 + {CE1D0620-BC75-456F-914B-3BEBF5444B4C}.Debug|x64.ActiveCfg = Debug|x64 + {CE1D0620-BC75-456F-914B-3BEBF5444B4C}.Debug|x64.Build.0 = Debug|x64 + {CE1D0620-BC75-456F-914B-3BEBF5444B4C}.Release|Win32.ActiveCfg = Release|Win32 + {CE1D0620-BC75-456F-914B-3BEBF5444B4C}.Release|Win32.Build.0 = Release|Win32 + {CE1D0620-BC75-456F-914B-3BEBF5444B4C}.Release|x64.ActiveCfg = Release|x64 + {CE1D0620-BC75-456F-914B-3BEBF5444B4C}.Release|x64.Build.0 = Release|x64 + {709B0E41-9792-4A0A-B28B-CBD06CE441B9}.Debug|Win32.ActiveCfg = Debug|Win32 + {709B0E41-9792-4A0A-B28B-CBD06CE441B9}.Debug|Win32.Build.0 = Debug|Win32 + {709B0E41-9792-4A0A-B28B-CBD06CE441B9}.Debug|x64.ActiveCfg = Debug|x64 + {709B0E41-9792-4A0A-B28B-CBD06CE441B9}.Debug|x64.Build.0 = Debug|x64 + {709B0E41-9792-4A0A-B28B-CBD06CE441B9}.Release|Win32.ActiveCfg = Release|Win32 + {709B0E41-9792-4A0A-B28B-CBD06CE441B9}.Release|Win32.Build.0 = Release|Win32 + {709B0E41-9792-4A0A-B28B-CBD06CE441B9}.Release|x64.ActiveCfg = Release|x64 + {709B0E41-9792-4A0A-B28B-CBD06CE441B9}.Release|x64.Build.0 = Release|x64 + {FAD361E1-1FD7-4993-BD20-7450026E51CC}.Debug|Win32.ActiveCfg = Debug|Win32 + {FAD361E1-1FD7-4993-BD20-7450026E51CC}.Debug|Win32.Build.0 = Debug|Win32 + {FAD361E1-1FD7-4993-BD20-7450026E51CC}.Debug|x64.ActiveCfg = Debug|x64 + {FAD361E1-1FD7-4993-BD20-7450026E51CC}.Debug|x64.Build.0 = Debug|x64 + {FAD361E1-1FD7-4993-BD20-7450026E51CC}.Release|Win32.ActiveCfg = Release|Win32 + {FAD361E1-1FD7-4993-BD20-7450026E51CC}.Release|Win32.Build.0 = Release|Win32 + {FAD361E1-1FD7-4993-BD20-7450026E51CC}.Release|x64.ActiveCfg = Release|x64 + {FAD361E1-1FD7-4993-BD20-7450026E51CC}.Release|x64.Build.0 = Release|x64 + {B6A37BAA-484D-4175-BEA2-62892A12E8F5}.Debug|Win32.ActiveCfg = Debug|Win32 + {B6A37BAA-484D-4175-BEA2-62892A12E8F5}.Debug|Win32.Build.0 = Debug|Win32 + {B6A37BAA-484D-4175-BEA2-62892A12E8F5}.Debug|x64.ActiveCfg = Debug|x64 + {B6A37BAA-484D-4175-BEA2-62892A12E8F5}.Debug|x64.Build.0 = Debug|x64 + {B6A37BAA-484D-4175-BEA2-62892A12E8F5}.Release|Win32.ActiveCfg = Release|Win32 + {B6A37BAA-484D-4175-BEA2-62892A12E8F5}.Release|Win32.Build.0 = Release|Win32 + {B6A37BAA-484D-4175-BEA2-62892A12E8F5}.Release|x64.ActiveCfg = Release|x64 + {B6A37BAA-484D-4175-BEA2-62892A12E8F5}.Release|x64.Build.0 = Release|x64 + {CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}.Debug|Win32.ActiveCfg = Debug|Win32 + {CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}.Debug|Win32.Build.0 = Debug|Win32 + {CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}.Debug|x64.ActiveCfg = Debug|x64 + {CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}.Debug|x64.Build.0 = Debug|x64 + {CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}.Release|Win32.ActiveCfg = Release|Win32 + {CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}.Release|Win32.Build.0 = Release|Win32 + {CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}.Release|x64.ActiveCfg = Release|x64 + {CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}.Release|x64.Build.0 = Release|x64 + {8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}.Debug|Win32.ActiveCfg = Debug|Win32 + {8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}.Debug|Win32.Build.0 = Debug|Win32 + {8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}.Debug|x64.ActiveCfg = Debug|x64 + {8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}.Debug|x64.Build.0 = Debug|x64 + {8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}.Release|Win32.ActiveCfg = Release|Win32 + {8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}.Release|Win32.Build.0 = Release|Win32 + {8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}.Release|x64.ActiveCfg = Release|x64 + {8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}.Release|x64.Build.0 = Release|x64 + {F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}.Debug|Win32.ActiveCfg = Debug|Win32 + {F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}.Debug|Win32.Build.0 = Debug|Win32 + {F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}.Debug|x64.ActiveCfg = Debug|x64 + {F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}.Debug|x64.Build.0 = Debug|x64 + {F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}.Release|Win32.ActiveCfg = Release|Win32 + {F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}.Release|Win32.Build.0 = Release|Win32 + {F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}.Release|x64.ActiveCfg = Release|x64 + {F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}.Release|x64.Build.0 = Release|x64 + {B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}.Debug|Win32.ActiveCfg = Debug|Win32 + {B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}.Debug|Win32.Build.0 = Debug|Win32 + {B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}.Debug|x64.ActiveCfg = Debug|x64 + {B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}.Debug|x64.Build.0 = Debug|x64 + {B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}.Release|Win32.ActiveCfg = Release|Win32 + {B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}.Release|Win32.Build.0 = Release|Win32 + {B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}.Release|x64.ActiveCfg = Release|x64 + {B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}.Release|x64.Build.0 = Release|x64 + {82EAF17E-9618-4BD7-AE50-0C325591B585}.Debug|Win32.ActiveCfg = Debug|Win32 + {82EAF17E-9618-4BD7-AE50-0C325591B585}.Debug|Win32.Build.0 = Debug|Win32 + {82EAF17E-9618-4BD7-AE50-0C325591B585}.Debug|x64.ActiveCfg = Debug|x64 + {82EAF17E-9618-4BD7-AE50-0C325591B585}.Debug|x64.Build.0 = Debug|x64 + {82EAF17E-9618-4BD7-AE50-0C325591B585}.Release|Win32.ActiveCfg = Release|Win32 + {82EAF17E-9618-4BD7-AE50-0C325591B585}.Release|Win32.Build.0 = Release|Win32 + {82EAF17E-9618-4BD7-AE50-0C325591B585}.Release|x64.ActiveCfg = Release|x64 + {82EAF17E-9618-4BD7-AE50-0C325591B585}.Release|x64.Build.0 = Release|x64 + {DA8A8F03-E719-45EF-A376-766A18772FA5}.Debug|Win32.ActiveCfg = Debug|Win32 + {DA8A8F03-E719-45EF-A376-766A18772FA5}.Debug|Win32.Build.0 = Debug|Win32 + {DA8A8F03-E719-45EF-A376-766A18772FA5}.Debug|x64.ActiveCfg = Debug|x64 + {DA8A8F03-E719-45EF-A376-766A18772FA5}.Debug|x64.Build.0 = Debug|x64 + {DA8A8F03-E719-45EF-A376-766A18772FA5}.Release|Win32.ActiveCfg = Release|Win32 + {DA8A8F03-E719-45EF-A376-766A18772FA5}.Release|Win32.Build.0 = Release|Win32 + {DA8A8F03-E719-45EF-A376-766A18772FA5}.Release|x64.ActiveCfg = Release|x64 + {DA8A8F03-E719-45EF-A376-766A18772FA5}.Release|x64.Build.0 = Release|x64 + {1FC3537C-EC13-4877-A06C-42DD8B81CBF3}.Debug|Win32.ActiveCfg = Debug|Win32 + {1FC3537C-EC13-4877-A06C-42DD8B81CBF3}.Debug|Win32.Build.0 = Debug|Win32 + {1FC3537C-EC13-4877-A06C-42DD8B81CBF3}.Debug|x64.ActiveCfg = Debug|x64 + {1FC3537C-EC13-4877-A06C-42DD8B81CBF3}.Debug|x64.Build.0 = Debug|x64 + {1FC3537C-EC13-4877-A06C-42DD8B81CBF3}.Release|Win32.ActiveCfg = Release|Win32 + {1FC3537C-EC13-4877-A06C-42DD8B81CBF3}.Release|Win32.Build.0 = Release|Win32 + {1FC3537C-EC13-4877-A06C-42DD8B81CBF3}.Release|x64.ActiveCfg = Release|x64 + {1FC3537C-EC13-4877-A06C-42DD8B81CBF3}.Release|x64.Build.0 = Release|x64 + {415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}.Debug|Win32.ActiveCfg = Debug|Win32 + {415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}.Debug|Win32.Build.0 = Debug|Win32 + {415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}.Debug|x64.ActiveCfg = Debug|x64 + {415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}.Debug|x64.Build.0 = Debug|x64 + {415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}.Release|Win32.ActiveCfg = Release|Win32 + {415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}.Release|Win32.Build.0 = Release|Win32 + {415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}.Release|x64.ActiveCfg = Release|x64 + {415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}.Release|x64.Build.0 = Release|x64 + {F401B9A2-B8CB-477A-A515-F029D0AA5553}.Debug|Win32.ActiveCfg = Debug|Win32 + {F401B9A2-B8CB-477A-A515-F029D0AA5553}.Debug|Win32.Build.0 = Debug|Win32 + {F401B9A2-B8CB-477A-A515-F029D0AA5553}.Debug|x64.ActiveCfg = Debug|x64 + {F401B9A2-B8CB-477A-A515-F029D0AA5553}.Debug|x64.Build.0 = Debug|x64 + {F401B9A2-B8CB-477A-A515-F029D0AA5553}.Release|Win32.ActiveCfg = Release|Win32 + {F401B9A2-B8CB-477A-A515-F029D0AA5553}.Release|Win32.Build.0 = Release|Win32 + {F401B9A2-B8CB-477A-A515-F029D0AA5553}.Release|x64.ActiveCfg = Release|x64 + {F401B9A2-B8CB-477A-A515-F029D0AA5553}.Release|x64.Build.0 = Release|x64 + {D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}.Debug|Win32.ActiveCfg = Debug|Win32 + {D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}.Debug|Win32.Build.0 = Debug|Win32 + {D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}.Debug|x64.ActiveCfg = Debug|x64 + {D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}.Debug|x64.Build.0 = Debug|x64 + {D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}.Release|Win32.ActiveCfg = Release|Win32 + {D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}.Release|Win32.Build.0 = Release|Win32 + {D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}.Release|x64.ActiveCfg = Release|x64 + {D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}.Release|x64.Build.0 = Release|x64 + {5E32A144-2F2D-4BB1-BBEF-13BE94414E99}.Debug|Win32.ActiveCfg = Debug|Win32 + {5E32A144-2F2D-4BB1-BBEF-13BE94414E99}.Debug|Win32.Build.0 = Debug|Win32 + {5E32A144-2F2D-4BB1-BBEF-13BE94414E99}.Debug|x64.ActiveCfg = Debug|x64 + {5E32A144-2F2D-4BB1-BBEF-13BE94414E99}.Debug|x64.Build.0 = Debug|x64 + {5E32A144-2F2D-4BB1-BBEF-13BE94414E99}.Release|Win32.ActiveCfg = Release|Win32 + {5E32A144-2F2D-4BB1-BBEF-13BE94414E99}.Release|Win32.Build.0 = Release|Win32 + {5E32A144-2F2D-4BB1-BBEF-13BE94414E99}.Release|x64.ActiveCfg = Release|x64 + {5E32A144-2F2D-4BB1-BBEF-13BE94414E99}.Release|x64.Build.0 = Release|x64 + {6CBACE55-8DDC-4EAE-A23A-DF412265D30C}.Debug|Win32.ActiveCfg = Debug|Win32 + {6CBACE55-8DDC-4EAE-A23A-DF412265D30C}.Debug|Win32.Build.0 = Debug|Win32 + {6CBACE55-8DDC-4EAE-A23A-DF412265D30C}.Debug|x64.ActiveCfg = Debug|x64 + {6CBACE55-8DDC-4EAE-A23A-DF412265D30C}.Debug|x64.Build.0 = Debug|x64 + {6CBACE55-8DDC-4EAE-A23A-DF412265D30C}.Release|Win32.ActiveCfg = Release|Win32 + {6CBACE55-8DDC-4EAE-A23A-DF412265D30C}.Release|Win32.Build.0 = Release|Win32 + {6CBACE55-8DDC-4EAE-A23A-DF412265D30C}.Release|x64.ActiveCfg = Release|x64 + {6CBACE55-8DDC-4EAE-A23A-DF412265D30C}.Release|x64.Build.0 = Release|x64 + {5C2B081E-5414-437B-86EB-B2695AEDF3F0}.Debug|Win32.ActiveCfg = Debug|Win32 + {5C2B081E-5414-437B-86EB-B2695AEDF3F0}.Debug|Win32.Build.0 = Debug|Win32 + {5C2B081E-5414-437B-86EB-B2695AEDF3F0}.Debug|x64.ActiveCfg = Debug|x64 + {5C2B081E-5414-437B-86EB-B2695AEDF3F0}.Debug|x64.Build.0 = Debug|x64 + {5C2B081E-5414-437B-86EB-B2695AEDF3F0}.Release|Win32.ActiveCfg = Release|Win32 + {5C2B081E-5414-437B-86EB-B2695AEDF3F0}.Release|Win32.Build.0 = Release|Win32 + {5C2B081E-5414-437B-86EB-B2695AEDF3F0}.Release|x64.ActiveCfg = Release|x64 + {5C2B081E-5414-437B-86EB-B2695AEDF3F0}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80} = {6712270F-F056-4512-883A-1756A25D90E1} + {114AC59B-BC28-40DB-8380-67C422D0C81B} = {6712270F-F056-4512-883A-1756A25D90E1} + {7870A9AC-92BB-423B-BC03-FBF7B46CD338} = {6712270F-F056-4512-883A-1756A25D90E1} + {50212477-1614-49C9-9791-4AC72025DC76} = {6712270F-F056-4512-883A-1756A25D90E1} + {CE1D0620-BC75-456F-914B-3BEBF5444B4C} = {6712270F-F056-4512-883A-1756A25D90E1} + {709B0E41-9792-4A0A-B28B-CBD06CE441B9} = {6712270F-F056-4512-883A-1756A25D90E1} + {FAD361E1-1FD7-4993-BD20-7450026E51CC} = {6712270F-F056-4512-883A-1756A25D90E1} + {B6A37BAA-484D-4175-BEA2-62892A12E8F5} = {6712270F-F056-4512-883A-1756A25D90E1} + {CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89} = {6712270F-F056-4512-883A-1756A25D90E1} + {8ECEF4F9-1461-4FCB-87D9-C871C71B01B7} = {6712270F-F056-4512-883A-1756A25D90E1} + {F8A9C956-AA19-4AEF-B1B7-E7C392E437FE} = {6712270F-F056-4512-883A-1756A25D90E1} + {B82FC407-B927-49D1-9DEB-0DFC3DC12A9C} = {6712270F-F056-4512-883A-1756A25D90E1} + {82EAF17E-9618-4BD7-AE50-0C325591B585} = {6712270F-F056-4512-883A-1756A25D90E1} + {DA8A8F03-E719-45EF-A376-766A18772FA5} = {6712270F-F056-4512-883A-1756A25D90E1} + {1FC3537C-EC13-4877-A06C-42DD8B81CBF3} = {6712270F-F056-4512-883A-1756A25D90E1} + {415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4} = {6712270F-F056-4512-883A-1756A25D90E1} + {F401B9A2-B8CB-477A-A515-F029D0AA5553} = {6712270F-F056-4512-883A-1756A25D90E1} + {D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2} = {6712270F-F056-4512-883A-1756A25D90E1} + {7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A} = {6712270F-F056-4512-883A-1756A25D90E1} + {5E32A144-2F2D-4BB1-BBEF-13BE94414E99} = {6712270F-F056-4512-883A-1756A25D90E1} + {6CBACE55-8DDC-4EAE-A23A-DF412265D30C} = {6712270F-F056-4512-883A-1756A25D90E1} + {5C2B081E-5414-437B-86EB-B2695AEDF3F0} = {6712270F-F056-4512-883A-1756A25D90E1} + EndGlobalSection +EndGlobal diff --git a/pe_bliss_tests_vc9.sln b/pe_bliss_tests_vc9.sln new file mode 100644 index 0000000..a8f324d --- /dev/null +++ b/pe_bliss_tests_vc9.sln @@ -0,0 +1,272 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_runner", "tests\test_runner\test_runner.vcproj", "{31843E48-DC9A-4887-BD97-328079D78C88}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{FB42AFF5-C8AA-495F-A397-E073D1A03BDE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_bound_import", "tests\test_bound_import\test_bound_import.vcproj", "{6EBEAFA6-7489-4026-83D1-CAF67D243119}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_checksum", "tests\test_checksum\test_checksum.vcproj", "{7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_dotnet", "tests\test_dotnet\test_dotnet.vcproj", "{094A7331-54E1-4034-BD1E-BE2F974B0142}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_debug", "tests\test_debug\test_debug.vcproj", "{42AC1521-0800-4D81-9363-6EF9362F7A4A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_entropy", "tests\test_entropy\test_entropy.vcproj", "{D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_exception_directory", "tests\test_exception_directory\test_exception_directory.vcproj", "{1C36ED94-CBE5-4107-83B6-9C37F3A4041C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_exports", "tests\test_exports\test_exports.vcproj", "{E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_imports", "tests\test_imports\test_imports.vcproj", "{BD969F96-E5A5-47B2-B5EF-B7999A441CE5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_load_config", "tests\test_load_config\test_load_config.vcproj", "{089E9482-33DD-4C64-84A1-C9B5F10F802A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_relocations", "tests\test_relocations\test_relocations.vcproj", "{997A89F0-372D-4306-AE4D-7438D93273C3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_bitmap", "tests\test_resource_bitmap\test_resource_bitmap.vcproj", "{1B337DC2-628E-4DA4-8C0F-A6880289C6E2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_icon_cursor", "tests\test_resource_icon_cursor\test_resource_icon_cursor.vcproj", "{1E59538C-2C78-4D35-8639-568890543A4A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_manager", "tests\test_resource_manager\test_resource_manager.vcproj", "{39E1826B-5436-47D3-9B95-D3C667691461}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_message_table", "tests\test_resource_message_table\test_resource_message_table.vcproj", "{57EFEFC9-E2D9-418E-9F05-3FD0D9921251}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_string_table", "tests\test_resource_string_table\test_resource_string_table.vcproj", "{C68A466D-0C1B-40BC-9AB1-49B582958524}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_version_info", "tests\test_resource_version_info\test_resource_version_info.vcproj", "{C30B270A-4C93-44A3-AABE-633713D0F1D7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resource_viewer", "tests\test_resource_viewer\test_resource_viewer.vcproj", "{DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_resources", "tests\test_resources\test_resources.vcproj", "{7E3867A9-59BC-4441-A74E-F4ABFFEE231C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_rich_data", "tests\test_rich_data\test_rich_data.vcproj", "{1F877026-3D94-41BF-B392-06DFAF67AE34}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_tls", "tests\test_tls\test_tls.vcproj", "{63D80BC8-EB14-4698-A391-4A41AC15E8D1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests_basic", "tests\tests_basic\tests_basic.vcproj", "{3451AE03-3363-445B-8DA8-94B197563D59}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests_utils", "tests\tests_utils\tests_utils.vcproj", "{B0478C25-73AD-4085-BA1A-DDF66431EB6E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {31843E48-DC9A-4887-BD97-328079D78C88}.Debug|Win32.ActiveCfg = Debug|Win32 + {31843E48-DC9A-4887-BD97-328079D78C88}.Debug|Win32.Build.0 = Debug|Win32 + {31843E48-DC9A-4887-BD97-328079D78C88}.Debug|x64.ActiveCfg = Debug|x64 + {31843E48-DC9A-4887-BD97-328079D78C88}.Debug|x64.Build.0 = Debug|x64 + {31843E48-DC9A-4887-BD97-328079D78C88}.Release|Win32.ActiveCfg = Release|Win32 + {31843E48-DC9A-4887-BD97-328079D78C88}.Release|Win32.Build.0 = Release|Win32 + {31843E48-DC9A-4887-BD97-328079D78C88}.Release|x64.ActiveCfg = Release|x64 + {31843E48-DC9A-4887-BD97-328079D78C88}.Release|x64.Build.0 = Release|x64 + {6EBEAFA6-7489-4026-83D1-CAF67D243119}.Debug|Win32.ActiveCfg = Debug|Win32 + {6EBEAFA6-7489-4026-83D1-CAF67D243119}.Debug|Win32.Build.0 = Debug|Win32 + {6EBEAFA6-7489-4026-83D1-CAF67D243119}.Debug|x64.ActiveCfg = Debug|x64 + {6EBEAFA6-7489-4026-83D1-CAF67D243119}.Debug|x64.Build.0 = Debug|x64 + {6EBEAFA6-7489-4026-83D1-CAF67D243119}.Release|Win32.ActiveCfg = Release|Win32 + {6EBEAFA6-7489-4026-83D1-CAF67D243119}.Release|Win32.Build.0 = Release|Win32 + {6EBEAFA6-7489-4026-83D1-CAF67D243119}.Release|x64.ActiveCfg = Release|x64 + {6EBEAFA6-7489-4026-83D1-CAF67D243119}.Release|x64.Build.0 = Release|x64 + {7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}.Debug|Win32.ActiveCfg = Debug|Win32 + {7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}.Debug|Win32.Build.0 = Debug|Win32 + {7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}.Debug|x64.ActiveCfg = Debug|x64 + {7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}.Debug|x64.Build.0 = Debug|x64 + {7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}.Release|Win32.ActiveCfg = Release|Win32 + {7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}.Release|Win32.Build.0 = Release|Win32 + {7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}.Release|x64.ActiveCfg = Release|x64 + {7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}.Release|x64.Build.0 = Release|x64 + {094A7331-54E1-4034-BD1E-BE2F974B0142}.Debug|Win32.ActiveCfg = Debug|Win32 + {094A7331-54E1-4034-BD1E-BE2F974B0142}.Debug|Win32.Build.0 = Debug|Win32 + {094A7331-54E1-4034-BD1E-BE2F974B0142}.Debug|x64.ActiveCfg = Debug|x64 + {094A7331-54E1-4034-BD1E-BE2F974B0142}.Debug|x64.Build.0 = Debug|x64 + {094A7331-54E1-4034-BD1E-BE2F974B0142}.Release|Win32.ActiveCfg = Release|Win32 + {094A7331-54E1-4034-BD1E-BE2F974B0142}.Release|Win32.Build.0 = Release|Win32 + {094A7331-54E1-4034-BD1E-BE2F974B0142}.Release|x64.ActiveCfg = Release|x64 + {094A7331-54E1-4034-BD1E-BE2F974B0142}.Release|x64.Build.0 = Release|x64 + {42AC1521-0800-4D81-9363-6EF9362F7A4A}.Debug|Win32.ActiveCfg = Debug|Win32 + {42AC1521-0800-4D81-9363-6EF9362F7A4A}.Debug|Win32.Build.0 = Debug|Win32 + {42AC1521-0800-4D81-9363-6EF9362F7A4A}.Debug|x64.ActiveCfg = Debug|x64 + {42AC1521-0800-4D81-9363-6EF9362F7A4A}.Debug|x64.Build.0 = Debug|x64 + {42AC1521-0800-4D81-9363-6EF9362F7A4A}.Release|Win32.ActiveCfg = Release|Win32 + {42AC1521-0800-4D81-9363-6EF9362F7A4A}.Release|Win32.Build.0 = Release|Win32 + {42AC1521-0800-4D81-9363-6EF9362F7A4A}.Release|x64.ActiveCfg = Release|x64 + {42AC1521-0800-4D81-9363-6EF9362F7A4A}.Release|x64.Build.0 = Release|x64 + {D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}.Debug|Win32.ActiveCfg = Debug|Win32 + {D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}.Debug|Win32.Build.0 = Debug|Win32 + {D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}.Debug|x64.ActiveCfg = Debug|x64 + {D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}.Debug|x64.Build.0 = Debug|x64 + {D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}.Release|Win32.ActiveCfg = Release|Win32 + {D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}.Release|Win32.Build.0 = Release|Win32 + {D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}.Release|x64.ActiveCfg = Release|x64 + {D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}.Release|x64.Build.0 = Release|x64 + {1C36ED94-CBE5-4107-83B6-9C37F3A4041C}.Debug|Win32.ActiveCfg = Debug|Win32 + {1C36ED94-CBE5-4107-83B6-9C37F3A4041C}.Debug|Win32.Build.0 = Debug|Win32 + {1C36ED94-CBE5-4107-83B6-9C37F3A4041C}.Debug|x64.ActiveCfg = Debug|x64 + {1C36ED94-CBE5-4107-83B6-9C37F3A4041C}.Debug|x64.Build.0 = Debug|x64 + {1C36ED94-CBE5-4107-83B6-9C37F3A4041C}.Release|Win32.ActiveCfg = Release|Win32 + {1C36ED94-CBE5-4107-83B6-9C37F3A4041C}.Release|Win32.Build.0 = Release|Win32 + {1C36ED94-CBE5-4107-83B6-9C37F3A4041C}.Release|x64.ActiveCfg = Release|x64 + {1C36ED94-CBE5-4107-83B6-9C37F3A4041C}.Release|x64.Build.0 = Release|x64 + {E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}.Debug|Win32.ActiveCfg = Debug|Win32 + {E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}.Debug|Win32.Build.0 = Debug|Win32 + {E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}.Debug|x64.ActiveCfg = Debug|x64 + {E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}.Debug|x64.Build.0 = Debug|x64 + {E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}.Release|Win32.ActiveCfg = Release|Win32 + {E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}.Release|Win32.Build.0 = Release|Win32 + {E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}.Release|x64.ActiveCfg = Release|x64 + {E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}.Release|x64.Build.0 = Release|x64 + {BD969F96-E5A5-47B2-B5EF-B7999A441CE5}.Debug|Win32.ActiveCfg = Debug|Win32 + {BD969F96-E5A5-47B2-B5EF-B7999A441CE5}.Debug|Win32.Build.0 = Debug|Win32 + {BD969F96-E5A5-47B2-B5EF-B7999A441CE5}.Debug|x64.ActiveCfg = Debug|x64 + {BD969F96-E5A5-47B2-B5EF-B7999A441CE5}.Debug|x64.Build.0 = Debug|x64 + {BD969F96-E5A5-47B2-B5EF-B7999A441CE5}.Release|Win32.ActiveCfg = Release|Win32 + {BD969F96-E5A5-47B2-B5EF-B7999A441CE5}.Release|Win32.Build.0 = Release|Win32 + {BD969F96-E5A5-47B2-B5EF-B7999A441CE5}.Release|x64.ActiveCfg = Release|x64 + {BD969F96-E5A5-47B2-B5EF-B7999A441CE5}.Release|x64.Build.0 = Release|x64 + {089E9482-33DD-4C64-84A1-C9B5F10F802A}.Debug|Win32.ActiveCfg = Debug|Win32 + {089E9482-33DD-4C64-84A1-C9B5F10F802A}.Debug|Win32.Build.0 = Debug|Win32 + {089E9482-33DD-4C64-84A1-C9B5F10F802A}.Debug|x64.ActiveCfg = Debug|x64 + {089E9482-33DD-4C64-84A1-C9B5F10F802A}.Debug|x64.Build.0 = Debug|x64 + {089E9482-33DD-4C64-84A1-C9B5F10F802A}.Release|Win32.ActiveCfg = Release|Win32 + {089E9482-33DD-4C64-84A1-C9B5F10F802A}.Release|Win32.Build.0 = Release|Win32 + {089E9482-33DD-4C64-84A1-C9B5F10F802A}.Release|x64.ActiveCfg = Release|x64 + {089E9482-33DD-4C64-84A1-C9B5F10F802A}.Release|x64.Build.0 = Release|x64 + {997A89F0-372D-4306-AE4D-7438D93273C3}.Debug|Win32.ActiveCfg = Debug|Win32 + {997A89F0-372D-4306-AE4D-7438D93273C3}.Debug|Win32.Build.0 = Debug|Win32 + {997A89F0-372D-4306-AE4D-7438D93273C3}.Debug|x64.ActiveCfg = Debug|x64 + {997A89F0-372D-4306-AE4D-7438D93273C3}.Debug|x64.Build.0 = Debug|x64 + {997A89F0-372D-4306-AE4D-7438D93273C3}.Release|Win32.ActiveCfg = Release|Win32 + {997A89F0-372D-4306-AE4D-7438D93273C3}.Release|Win32.Build.0 = Release|Win32 + {997A89F0-372D-4306-AE4D-7438D93273C3}.Release|x64.ActiveCfg = Release|x64 + {997A89F0-372D-4306-AE4D-7438D93273C3}.Release|x64.Build.0 = Release|x64 + {1B337DC2-628E-4DA4-8C0F-A6880289C6E2}.Debug|Win32.ActiveCfg = Debug|Win32 + {1B337DC2-628E-4DA4-8C0F-A6880289C6E2}.Debug|Win32.Build.0 = Debug|Win32 + {1B337DC2-628E-4DA4-8C0F-A6880289C6E2}.Debug|x64.ActiveCfg = Debug|x64 + {1B337DC2-628E-4DA4-8C0F-A6880289C6E2}.Debug|x64.Build.0 = Debug|x64 + {1B337DC2-628E-4DA4-8C0F-A6880289C6E2}.Release|Win32.ActiveCfg = Release|Win32 + {1B337DC2-628E-4DA4-8C0F-A6880289C6E2}.Release|Win32.Build.0 = Release|Win32 + {1B337DC2-628E-4DA4-8C0F-A6880289C6E2}.Release|x64.ActiveCfg = Release|x64 + {1B337DC2-628E-4DA4-8C0F-A6880289C6E2}.Release|x64.Build.0 = Release|x64 + {1E59538C-2C78-4D35-8639-568890543A4A}.Debug|Win32.ActiveCfg = Debug|Win32 + {1E59538C-2C78-4D35-8639-568890543A4A}.Debug|Win32.Build.0 = Debug|Win32 + {1E59538C-2C78-4D35-8639-568890543A4A}.Debug|x64.ActiveCfg = Debug|x64 + {1E59538C-2C78-4D35-8639-568890543A4A}.Debug|x64.Build.0 = Debug|x64 + {1E59538C-2C78-4D35-8639-568890543A4A}.Release|Win32.ActiveCfg = Release|Win32 + {1E59538C-2C78-4D35-8639-568890543A4A}.Release|Win32.Build.0 = Release|Win32 + {1E59538C-2C78-4D35-8639-568890543A4A}.Release|x64.ActiveCfg = Release|x64 + {1E59538C-2C78-4D35-8639-568890543A4A}.Release|x64.Build.0 = Release|x64 + {39E1826B-5436-47D3-9B95-D3C667691461}.Debug|Win32.ActiveCfg = Debug|Win32 + {39E1826B-5436-47D3-9B95-D3C667691461}.Debug|Win32.Build.0 = Debug|Win32 + {39E1826B-5436-47D3-9B95-D3C667691461}.Debug|x64.ActiveCfg = Debug|x64 + {39E1826B-5436-47D3-9B95-D3C667691461}.Debug|x64.Build.0 = Debug|x64 + {39E1826B-5436-47D3-9B95-D3C667691461}.Release|Win32.ActiveCfg = Release|Win32 + {39E1826B-5436-47D3-9B95-D3C667691461}.Release|Win32.Build.0 = Release|Win32 + {39E1826B-5436-47D3-9B95-D3C667691461}.Release|x64.ActiveCfg = Release|x64 + {39E1826B-5436-47D3-9B95-D3C667691461}.Release|x64.Build.0 = Release|x64 + {57EFEFC9-E2D9-418E-9F05-3FD0D9921251}.Debug|Win32.ActiveCfg = Debug|Win32 + {57EFEFC9-E2D9-418E-9F05-3FD0D9921251}.Debug|Win32.Build.0 = Debug|Win32 + {57EFEFC9-E2D9-418E-9F05-3FD0D9921251}.Debug|x64.ActiveCfg = Debug|x64 + {57EFEFC9-E2D9-418E-9F05-3FD0D9921251}.Debug|x64.Build.0 = Debug|x64 + {57EFEFC9-E2D9-418E-9F05-3FD0D9921251}.Release|Win32.ActiveCfg = Release|Win32 + {57EFEFC9-E2D9-418E-9F05-3FD0D9921251}.Release|Win32.Build.0 = Release|Win32 + {57EFEFC9-E2D9-418E-9F05-3FD0D9921251}.Release|x64.ActiveCfg = Release|x64 + {57EFEFC9-E2D9-418E-9F05-3FD0D9921251}.Release|x64.Build.0 = Release|x64 + {C68A466D-0C1B-40BC-9AB1-49B582958524}.Debug|Win32.ActiveCfg = Debug|Win32 + {C68A466D-0C1B-40BC-9AB1-49B582958524}.Debug|Win32.Build.0 = Debug|Win32 + {C68A466D-0C1B-40BC-9AB1-49B582958524}.Debug|x64.ActiveCfg = Debug|x64 + {C68A466D-0C1B-40BC-9AB1-49B582958524}.Debug|x64.Build.0 = Debug|x64 + {C68A466D-0C1B-40BC-9AB1-49B582958524}.Release|Win32.ActiveCfg = Release|Win32 + {C68A466D-0C1B-40BC-9AB1-49B582958524}.Release|Win32.Build.0 = Release|Win32 + {C68A466D-0C1B-40BC-9AB1-49B582958524}.Release|x64.ActiveCfg = Release|x64 + {C68A466D-0C1B-40BC-9AB1-49B582958524}.Release|x64.Build.0 = Release|x64 + {C30B270A-4C93-44A3-AABE-633713D0F1D7}.Debug|Win32.ActiveCfg = Debug|Win32 + {C30B270A-4C93-44A3-AABE-633713D0F1D7}.Debug|Win32.Build.0 = Debug|Win32 + {C30B270A-4C93-44A3-AABE-633713D0F1D7}.Debug|x64.ActiveCfg = Debug|x64 + {C30B270A-4C93-44A3-AABE-633713D0F1D7}.Debug|x64.Build.0 = Debug|x64 + {C30B270A-4C93-44A3-AABE-633713D0F1D7}.Release|Win32.ActiveCfg = Release|Win32 + {C30B270A-4C93-44A3-AABE-633713D0F1D7}.Release|Win32.Build.0 = Release|Win32 + {C30B270A-4C93-44A3-AABE-633713D0F1D7}.Release|x64.ActiveCfg = Release|x64 + {C30B270A-4C93-44A3-AABE-633713D0F1D7}.Release|x64.Build.0 = Release|x64 + {DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}.Debug|Win32.ActiveCfg = Debug|Win32 + {DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}.Debug|Win32.Build.0 = Debug|Win32 + {DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}.Debug|x64.ActiveCfg = Debug|x64 + {DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}.Debug|x64.Build.0 = Debug|x64 + {DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}.Release|Win32.ActiveCfg = Release|Win32 + {DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}.Release|Win32.Build.0 = Release|Win32 + {DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}.Release|x64.ActiveCfg = Release|x64 + {DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}.Release|x64.Build.0 = Release|x64 + {7E3867A9-59BC-4441-A74E-F4ABFFEE231C}.Debug|Win32.ActiveCfg = Debug|Win32 + {7E3867A9-59BC-4441-A74E-F4ABFFEE231C}.Debug|Win32.Build.0 = Debug|Win32 + {7E3867A9-59BC-4441-A74E-F4ABFFEE231C}.Debug|x64.ActiveCfg = Debug|x64 + {7E3867A9-59BC-4441-A74E-F4ABFFEE231C}.Debug|x64.Build.0 = Debug|x64 + {7E3867A9-59BC-4441-A74E-F4ABFFEE231C}.Release|Win32.ActiveCfg = Release|Win32 + {7E3867A9-59BC-4441-A74E-F4ABFFEE231C}.Release|Win32.Build.0 = Release|Win32 + {7E3867A9-59BC-4441-A74E-F4ABFFEE231C}.Release|x64.ActiveCfg = Release|x64 + {7E3867A9-59BC-4441-A74E-F4ABFFEE231C}.Release|x64.Build.0 = Release|x64 + {1F877026-3D94-41BF-B392-06DFAF67AE34}.Debug|Win32.ActiveCfg = Debug|Win32 + {1F877026-3D94-41BF-B392-06DFAF67AE34}.Debug|Win32.Build.0 = Debug|Win32 + {1F877026-3D94-41BF-B392-06DFAF67AE34}.Debug|x64.ActiveCfg = Debug|x64 + {1F877026-3D94-41BF-B392-06DFAF67AE34}.Debug|x64.Build.0 = Debug|x64 + {1F877026-3D94-41BF-B392-06DFAF67AE34}.Release|Win32.ActiveCfg = Release|Win32 + {1F877026-3D94-41BF-B392-06DFAF67AE34}.Release|Win32.Build.0 = Release|Win32 + {1F877026-3D94-41BF-B392-06DFAF67AE34}.Release|x64.ActiveCfg = Release|x64 + {1F877026-3D94-41BF-B392-06DFAF67AE34}.Release|x64.Build.0 = Release|x64 + {63D80BC8-EB14-4698-A391-4A41AC15E8D1}.Debug|Win32.ActiveCfg = Debug|Win32 + {63D80BC8-EB14-4698-A391-4A41AC15E8D1}.Debug|Win32.Build.0 = Debug|Win32 + {63D80BC8-EB14-4698-A391-4A41AC15E8D1}.Debug|x64.ActiveCfg = Debug|x64 + {63D80BC8-EB14-4698-A391-4A41AC15E8D1}.Debug|x64.Build.0 = Debug|x64 + {63D80BC8-EB14-4698-A391-4A41AC15E8D1}.Release|Win32.ActiveCfg = Release|Win32 + {63D80BC8-EB14-4698-A391-4A41AC15E8D1}.Release|Win32.Build.0 = Release|Win32 + {63D80BC8-EB14-4698-A391-4A41AC15E8D1}.Release|x64.ActiveCfg = Release|x64 + {63D80BC8-EB14-4698-A391-4A41AC15E8D1}.Release|x64.Build.0 = Release|x64 + {3451AE03-3363-445B-8DA8-94B197563D59}.Debug|Win32.ActiveCfg = Debug|Win32 + {3451AE03-3363-445B-8DA8-94B197563D59}.Debug|Win32.Build.0 = Debug|Win32 + {3451AE03-3363-445B-8DA8-94B197563D59}.Debug|x64.ActiveCfg = Debug|x64 + {3451AE03-3363-445B-8DA8-94B197563D59}.Debug|x64.Build.0 = Debug|x64 + {3451AE03-3363-445B-8DA8-94B197563D59}.Release|Win32.ActiveCfg = Release|Win32 + {3451AE03-3363-445B-8DA8-94B197563D59}.Release|Win32.Build.0 = Release|Win32 + {3451AE03-3363-445B-8DA8-94B197563D59}.Release|x64.ActiveCfg = Release|x64 + {3451AE03-3363-445B-8DA8-94B197563D59}.Release|x64.Build.0 = Release|x64 + {B0478C25-73AD-4085-BA1A-DDF66431EB6E}.Debug|Win32.ActiveCfg = Debug|Win32 + {B0478C25-73AD-4085-BA1A-DDF66431EB6E}.Debug|Win32.Build.0 = Debug|Win32 + {B0478C25-73AD-4085-BA1A-DDF66431EB6E}.Debug|x64.ActiveCfg = Debug|x64 + {B0478C25-73AD-4085-BA1A-DDF66431EB6E}.Debug|x64.Build.0 = Debug|x64 + {B0478C25-73AD-4085-BA1A-DDF66431EB6E}.Release|Win32.ActiveCfg = Release|Win32 + {B0478C25-73AD-4085-BA1A-DDF66431EB6E}.Release|Win32.Build.0 = Release|Win32 + {B0478C25-73AD-4085-BA1A-DDF66431EB6E}.Release|x64.ActiveCfg = Release|x64 + {B0478C25-73AD-4085-BA1A-DDF66431EB6E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6EBEAFA6-7489-4026-83D1-CAF67D243119} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {094A7331-54E1-4034-BD1E-BE2F974B0142} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {42AC1521-0800-4D81-9363-6EF9362F7A4A} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {1C36ED94-CBE5-4107-83B6-9C37F3A4041C} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {E126644C-38FF-41EF-9EAF-ED8C9FCF62EF} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {BD969F96-E5A5-47B2-B5EF-B7999A441CE5} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {089E9482-33DD-4C64-84A1-C9B5F10F802A} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {997A89F0-372D-4306-AE4D-7438D93273C3} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {1B337DC2-628E-4DA4-8C0F-A6880289C6E2} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {1E59538C-2C78-4D35-8639-568890543A4A} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {39E1826B-5436-47D3-9B95-D3C667691461} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {57EFEFC9-E2D9-418E-9F05-3FD0D9921251} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {C68A466D-0C1B-40BC-9AB1-49B582958524} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {C30B270A-4C93-44A3-AABE-633713D0F1D7} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {7E3867A9-59BC-4441-A74E-F4ABFFEE231C} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {1F877026-3D94-41BF-B392-06DFAF67AE34} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {63D80BC8-EB14-4698-A391-4A41AC15E8D1} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {3451AE03-3363-445B-8DA8-94B197563D59} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + {B0478C25-73AD-4085-BA1A-DDF66431EB6E} = {FB42AFF5-C8AA-495F-A397-E073D1A03BDE} + EndGlobalSection +EndGlobal diff --git a/pe_lib_vc10.sln b/pe_bliss_vc10.sln similarity index 97% rename from pe_lib_vc10.sln rename to pe_bliss_vc10.sln index a7d1148..a6a22d3 100644 --- a/pe_lib_vc10.sln +++ b/pe_bliss_vc10.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pe_lib", "pe_lib\pe_lib.vcxproj", "{1461F543-D1FA-4E4C-B6D7-0F879F566035}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pe_bliss", "pe_lib\pe_lib.vcxproj", "{1461F543-D1FA-4E4C-B6D7-0F879F566035}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "View PE information", "View PE information", "{49B3F007-A569-40AE-8FB4-A2AE0347F479}" EndProject @@ -144,6 +144,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "full_pe_rebuilder", "sample {1461F543-D1FA-4E4C-B6D7-0F879F566035} = {1461F543-D1FA-4E4C-B6D7-0F879F566035} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{65326322-DFB0-46BF-80A6-84B8A7C9628D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -409,5 +411,7 @@ Global {B88455A6-C93A-4F17-93A5-FC84B70F9CFE} = {53A03406-AEFB-4ABA-8DD1-5AE9B8DFB036} {48B99169-44E6-41E5-A681-78243B885E86} = {53A03406-AEFB-4ABA-8DD1-5AE9B8DFB036} {D7F6EE93-F88A-4B66-8761-87EC844E20C5} = {53A03406-AEFB-4ABA-8DD1-5AE9B8DFB036} + {53A03406-AEFB-4ABA-8DD1-5AE9B8DFB036} = {65326322-DFB0-46BF-80A6-84B8A7C9628D} + {49B3F007-A569-40AE-8FB4-A2AE0347F479} = {65326322-DFB0-46BF-80A6-84B8A7C9628D} EndGlobalSection EndGlobal diff --git a/pe_lib_vc9.sln b/pe_bliss_vc9.sln similarity index 97% rename from pe_lib_vc9.sln rename to pe_bliss_vc9.sln index 999ffe8..e554f51 100644 --- a/pe_lib_vc9.sln +++ b/pe_bliss_vc9.sln @@ -144,6 +144,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "full_pe_rebuilder", "sample {4B658F8F-1722-4EEA-880C-A4A64DCA9F2C} = {4B658F8F-1722-4EEA-880C-A4A64DCA9F2C} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{9191A7C3-3B0A-45A5-99A2-72D1903AA7B7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -409,5 +411,7 @@ Global {781B56DF-15CE-4CBA-A008-0403029E558A} = {0CF51D6D-1F80-4D2C-95BE-566641BD1F71} {22788F46-AB6B-4278-B1C0-ED220AE85F4A} = {0CF51D6D-1F80-4D2C-95BE-566641BD1F71} {F61AB18A-8E8B-43D1-91B2-D6A2A297FDD6} = {0CF51D6D-1F80-4D2C-95BE-566641BD1F71} + {0CF51D6D-1F80-4D2C-95BE-566641BD1F71} = {9191A7C3-3B0A-45A5-99A2-72D1903AA7B7} + {25A7C026-702D-48A4-A1EC-81DE78F0664E} = {9191A7C3-3B0A-45A5-99A2-72D1903AA7B7} EndGlobalSection EndGlobal diff --git a/pe_lib/Makefile b/pe_lib/Makefile index 8dfa3ff..6105f28 100644 --- a/pe_lib/Makefile +++ b/pe_lib/Makefile @@ -1,4 +1,4 @@ -OBJS = pe_base.o pe_32_64.o pe_factory.o pe_resource_manager.o pe_exception.o +OBJS = entropy.o file_version_info.o message_table.o pe_base.o pe_bound_import.o pe_checksum.o pe_debug.o pe_directory.o pe_dotnet.o pe_exception_directory.o pe_exports.o pe_imports.o pe_load_config.o pe_properties.o pe_properties_generic.o pe_relocations.o pe_factory.o pe_resources.o pe_resource_manager.o pe_resource_viewer.o pe_rich_data.o pe_section.o pe_tls.o utils.o version_info_editor.o version_info_viewer.o pe_exception.o resource_message_list_reader.o resource_string_table_reader.o resource_version_info_reader.o resource_version_info_writer.o resource_cursor_icon_reader.o resource_cursor_icon_writer.o resource_bitmap_writer.o resource_bitmap_reader.o resource_data_info.o pe_rebuilder.o LIBNAME = pebliss LIBPATH = ../lib CXXFLAGS = -O2 -Wall -fPIC -DPIC -I. diff --git a/pe_lib/entropy.cpp b/pe_lib/entropy.cpp new file mode 100644 index 0000000..e045562 --- /dev/null +++ b/pe_lib/entropy.cpp @@ -0,0 +1,90 @@ +#include <cmath> +#include "entropy.h" +#include "utils.h" + +namespace pe_bliss +{ +//Calculates entropy for PE image section +double entropy_calculator::calculate_entropy(const section& s) +{ + if(s.get_raw_data().empty()) //Don't count entropy for empty sections + throw pe_exception("Section is empty", pe_exception::section_is_empty); + + return calculate_entropy(s.get_raw_data().data(), s.get_raw_data().length()); +} + +//Calculates entropy for istream (from current position of stream) +double entropy_calculator::calculate_entropy(std::istream& file) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + if(file.bad()) + throw pe_exception("Stream is bad", pe_exception::stream_is_bad); + + std::streamoff pos = file.tellg(); + + std::streamoff length = pe_utils::get_file_size(file); + length -= file.tellg(); + + if(!length) //Don't calculate entropy for empty buffers + throw pe_exception("Data length is zero", pe_exception::data_is_empty); + + //Count bytes + for(std::streamoff i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(file.get())]; + + file.seekg(pos); + + return calculate_entropy(byte_count, length); +} + +//Calculates entropy for data block +double entropy_calculator::calculate_entropy(const char* data, size_t length) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + if(!length) //Don't calculate entropy for empty buffers + throw pe_exception("Data length is zero", pe_exception::data_is_empty); + + //Count bytes + for(size_t i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(data[i])]; + + return calculate_entropy(byte_count, length); +} + +//Calculates entropy for this PE file (only section data) +double entropy_calculator::calculate_entropy(const pe_base& pe) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + size_t total_data_length = 0; + + //Count bytes for each section + for(section_list::const_iterator it = pe.get_image_sections().begin(); it != pe.get_image_sections().end(); ++it) + { + const std::string& data = (*it).get_raw_data(); + size_t length = data.length(); + total_data_length += length; + for(size_t i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(data[i])]; + } + + return calculate_entropy(byte_count, total_data_length); +} + +//Calculates entropy from bytes count +double entropy_calculator::calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length) +{ + double entropy = 0.; //Entropy result value + //Calculate entropy + for(uint32_t i = 0; i < 256; ++i) + { + double temp = static_cast<double>(byte_count[i]) / total_length; + if(temp > 0.) + entropy += std::abs(temp * (std::log(temp) * pe_utils::log_2)); + } + + return entropy; +} +} diff --git a/pe_lib/entropy.h b/pe_lib/entropy.h new file mode 100644 index 0000000..c38f192 --- /dev/null +++ b/pe_lib/entropy.h @@ -0,0 +1,30 @@ +#pragma once +#include <istream> +#include "pe_base.h" + +namespace pe_bliss +{ +class entropy_calculator +{ +public: + //Calculates entropy for PE image section + static double calculate_entropy(const section& s); + + //Calculates entropy for istream (from current position of stream) + static double calculate_entropy(std::istream& file); + + //Calculates entropy for data block + static double calculate_entropy(const char* data, size_t length); + + //Calculates entropy for this PE file (only section data) + static double calculate_entropy(const pe_base& pe); + +private: + entropy_calculator(); + entropy_calculator(const entropy_calculator&); + entropy_calculator& operator=(const entropy_calculator&); + + //Calculates entropy from bytes count + static double calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length); +}; +} diff --git a/pe_lib/file_version_info.cpp b/pe_lib/file_version_info.cpp new file mode 100644 index 0000000..8abf9af --- /dev/null +++ b/pe_lib/file_version_info.cpp @@ -0,0 +1,419 @@ +#include "file_version_info.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Default constructor +file_version_info::file_version_info() + :file_version_ms_(0), file_version_ls_(0), + product_version_ms_(0), product_version_ls_(0), + file_flags_(0), + file_os_(0), + file_type_(0), file_subtype_(0), + file_date_ms_(0), file_date_ls_(0) +{} + +//Constructor from Windows fixed version info structure +file_version_info::file_version_info(const vs_fixedfileinfo& info) + :file_version_ms_(info.dwFileVersionMS), file_version_ls_(info.dwFileVersionLS), + product_version_ms_(info.dwProductVersionMS), product_version_ls_(info.dwProductVersionLS), + file_flags_(info.dwFileFlags), + file_os_(info.dwFileOS), + file_type_(info.dwFileType), file_subtype_(info.dwFileSubtype), + file_date_ms_(info.dwFileDateMS), file_date_ls_(info.dwFileDateLS) +{} + +//Returns true if file is debug-built +bool file_version_info::is_debug() const +{ + return file_flags_ & vs_ff_debug ? true : false; +} + +//Returns true if file is release-built +bool file_version_info::is_prerelease() const +{ + return file_flags_ & vs_ff_prerelease ? true : false; +} + +//Returns true if file is patched +bool file_version_info::is_patched() const +{ + return file_flags_ & vs_ff_patched ? true : false; +} + +//Returns true if private build +bool file_version_info::is_private_build() const +{ + return file_flags_ & vs_ff_privatebuild ? true : false; +} + +//Returns true if special build +bool file_version_info::is_special_build() const +{ + return file_flags_ & vs_ff_specialbuild ? true : false; +} + +//Returns true if info inferred +bool file_version_info::is_info_inferred() const +{ + return file_flags_ & vs_ff_infoinferred ? true : false; +} + +//Retuens file flags (raw DWORD) +uint32_t file_version_info::get_file_flags() const +{ + return file_flags_; +} + +//Returns file version most significant DWORD +uint32_t file_version_info::get_file_version_ms() const +{ + return file_version_ms_; +} + +//Returns file version least significant DWORD +uint32_t file_version_info::get_file_version_ls() const +{ + return file_version_ls_; +} + +//Returns product version most significant DWORD +uint32_t file_version_info::get_product_version_ms() const +{ + return product_version_ms_; +} + +//Returns product version least significant DWORD +uint32_t file_version_info::get_product_version_ls() const +{ + return product_version_ls_; +} + +//Returns file OS type (raw DWORD) +uint32_t file_version_info::get_file_os_raw() const +{ + return file_os_; +} + +//Returns file OS type +file_version_info::file_os_type file_version_info::get_file_os() const +{ + //Determine file operation system type + switch(file_os_) + { + case vos_dos: + return file_os_dos; + + case vos_os216: + return file_os_os216; + + case vos_os232: + return file_os_os232; + + case vos_nt: + return file_os_nt; + + case vos_wince: + return file_os_wince; + + case vos__windows16: + return file_os_win16; + + case vos__pm16: + return file_os_pm16; + + case vos__pm32: + return file_os_pm32; + + case vos__windows32: + return file_os_win32; + + case vos_dos_windows16: + return file_os_dos_win16; + + case vos_dos_windows32: + return file_os_dos_win32; + + case vos_os216_pm16: + return file_os_os216_pm16; + + case vos_os232_pm32: + return file_os_os232_pm32; + + case vos_nt_windows32: + return file_os_nt_win32; + } + + return file_os_unknown; +} + +//Returns file type (raw DWORD) +uint32_t file_version_info::get_file_type_raw() const +{ + return file_type_; +} + +//Returns file type +file_version_info::file_type file_version_info::get_file_type() const +{ + //Determine file type + switch(file_type_) + { + case vft_app: + return file_type_application; + + case vft_dll: + return file_type_dll; + + case vft_drv: + return file_type_driver; + + case vft_font: + return file_type_font; + + case vft_vxd: + return file_type_vxd; + + case vft_static_lib: + return file_type_static_lib; + } + + return file_type_unknown; +} + +//Returns file subtype (usually non-zero for drivers and fonts) +uint32_t file_version_info::get_file_subtype() const +{ + return file_subtype_; +} + +//Returns file date most significant DWORD +uint32_t file_version_info::get_file_date_ms() const +{ + return file_date_ms_; +} + +//Returns file date least significant DWORD +uint32_t file_version_info::get_file_date_ls() const +{ + return file_date_ls_; +} + +//Helper to set file flag +void file_version_info::set_file_flag(uint32_t flag) +{ + file_flags_ |= flag; +} + +//Helper to clear file flag +void file_version_info::clear_file_flag(uint32_t flag) +{ + file_flags_ &= ~flag; +} + +//Helper to set or clear file flag +void file_version_info::set_file_flag(uint32_t flag, bool set_flag) +{ + set_flag ? set_file_flag(flag) : clear_file_flag(flag); +} + +//Sets if file is debug-built +void file_version_info::set_debug(bool debug) +{ + set_file_flag(vs_ff_debug, debug); +} + +//Sets if file is prerelease +void file_version_info::set_prerelease(bool prerelease) +{ + set_file_flag(vs_ff_prerelease, prerelease); +} + +//Sets if file is patched +void file_version_info::set_patched(bool patched) +{ + set_file_flag(vs_ff_patched, patched); +} + +//Sets if private build +void file_version_info::set_private_build(bool private_build) +{ + set_file_flag(vs_ff_privatebuild, private_build); +} + +//Sets if special build +void file_version_info::set_special_build(bool special_build) +{ + set_file_flag(vs_ff_specialbuild, special_build); +} + +//Sets if info inferred +void file_version_info::set_info_inferred(bool info_inferred) +{ + set_file_flag(vs_ff_infoinferred, info_inferred); +} + +//Sets flags (raw DWORD) +void file_version_info::set_file_flags(uint32_t file_flags) +{ + file_flags_ = file_flags; +} + +//Sets file version most significant DWORD +void file_version_info::set_file_version_ms(uint32_t file_version_ms) +{ + file_version_ms_ = file_version_ms; +} + +//Sets file version least significant DWORD +void file_version_info::set_file_version_ls(uint32_t file_version_ls) +{ + file_version_ls_ = file_version_ls; +} + +//Sets product version most significant DWORD +void file_version_info::set_product_version_ms(uint32_t product_version_ms) +{ + product_version_ms_ = product_version_ms; +} + +//Sets product version least significant DWORD +void file_version_info::set_product_version_ls(uint32_t product_version_ls) +{ + product_version_ls_ = product_version_ls; +} + +//Sets file OS type (raw DWORD) +void file_version_info::set_file_os_raw(uint32_t file_os) +{ + file_os_ = file_os; +} + +//Sets file OS type +void file_version_info::set_file_os(file_os_type file_os) +{ + //Determine file operation system type + switch(file_os) + { + case file_os_dos: + file_os_ = vos_dos; + return; + + case file_os_os216: + file_os_ = vos_os216; + return; + + case file_os_os232: + file_os_ = vos_os232; + return; + + case file_os_nt: + file_os_ = vos_nt; + return; + + case file_os_wince: + file_os_ = vos_wince; + return; + + case file_os_win16: + file_os_ = vos__windows16; + return; + + case file_os_pm16: + file_os_ = vos__pm16; + return; + + case file_os_pm32: + file_os_ = vos__pm32; + return; + + case file_os_win32: + file_os_ = vos__windows32; + return; + + case file_os_dos_win16: + file_os_ = vos_dos_windows16; + return; + + case file_os_dos_win32: + file_os_ = vos_dos_windows32; + return; + + case file_os_os216_pm16: + file_os_ = vos_os216_pm16; + return; + + case file_os_os232_pm32: + file_os_ = vos_os232_pm32; + return; + + case file_os_nt_win32: + file_os_ = vos_nt_windows32; + return; + + default: + return; + } +} + +//Sets file type (raw DWORD) +void file_version_info::set_file_type_raw(uint32_t file_type) +{ + file_type_ = file_type; +} + +//Sets file type +void file_version_info::set_file_type(file_type file_type) +{ + //Determine file type + switch(file_type) + { + case file_type_application: + file_type_ = vft_app; + return; + + case file_type_dll: + file_type_ = vft_dll; + return; + + case file_type_driver: + file_type_ = vft_drv; + return; + + case file_type_font: + file_type_ = vft_font; + return; + + case file_type_vxd: + file_type_ = vft_vxd; + return; + + case file_type_static_lib: + file_type_ = vft_static_lib; + return; + + default: + return; + } +} + +//Sets file subtype (usually non-zero for drivers and fonts) +void file_version_info::set_file_subtype(uint32_t file_subtype) +{ + file_subtype_ = file_subtype; +} + +//Sets file date most significant DWORD +void file_version_info::set_file_date_ms(uint32_t file_date_ms) +{ + file_date_ms_ = file_date_ms; +} + +//Sets file date least significant DWORD +void file_version_info::set_file_date_ls(uint32_t file_date_ls) +{ + file_date_ls_ = file_date_ls; +} +} diff --git a/pe_lib/file_version_info.h b/pe_lib/file_version_info.h new file mode 100644 index 0000000..735a422 --- /dev/null +++ b/pe_lib/file_version_info.h @@ -0,0 +1,178 @@ +#pragma once +#include <string> +#include <map> +#include "stdint_defs.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +//Structure representing fixed file version info +class file_version_info +{ +public: + //Enumeration of file operating system types + enum file_os_type + { + file_os_unknown, + file_os_dos, + file_os_os216, + file_os_os232, + file_os_nt, + file_os_wince, + file_os_win16, + file_os_pm16, + file_os_pm32, + file_os_win32, + file_os_dos_win16, + file_os_dos_win32, + file_os_os216_pm16, + file_os_os232_pm32, + file_os_nt_win32 + }; + + //Enumeration of file types + enum file_type + { + file_type_unknown, + file_type_application, + file_type_dll, + file_type_driver, + file_type_font, + file_type_vxd, + file_type_static_lib + }; + +public: + //Default constructor + file_version_info(); + //Constructor from Windows fixed version info structure + explicit file_version_info(const pe_win::vs_fixedfileinfo& info); + +public: //Getters + //Returns true if file is debug-built + bool is_debug() const; + //Returns true if file is prerelease + bool is_prerelease() const; + //Returns true if file is patched + bool is_patched() const; + //Returns true if private build + bool is_private_build() const; + //Returns true if special build + bool is_special_build() const; + //Returns true if info inferred + bool is_info_inferred() const; + //Retuens file flags (raw DWORD) + uint32_t get_file_flags() const; + + //Returns file version most significant DWORD + uint32_t get_file_version_ms() const; + //Returns file version least significant DWORD + uint32_t get_file_version_ls() const; + //Returns product version most significant DWORD + uint32_t get_product_version_ms() const; + //Returns product version least significant DWORD + uint32_t get_product_version_ls() const; + + //Returns file OS type (raw DWORD) + uint32_t get_file_os_raw() const; + //Returns file OS type + file_os_type get_file_os() const; + + //Returns file type (raw DWORD) + uint32_t get_file_type_raw() const; + //Returns file type + file_type get_file_type() const; + + //Returns file subtype (usually non-zero for drivers and fonts) + uint32_t get_file_subtype() const; + + //Returns file date most significant DWORD + uint32_t get_file_date_ms() const; + //Returns file date least significant DWORD + uint32_t get_file_date_ls() const; + + //Returns file version string + template<typename T> + const std::basic_string<T> get_file_version_string() const + { + return get_version_string<T>(file_version_ms_, file_version_ls_); + } + + //Returns product version string + template<typename T> + const std::basic_string<T> get_product_version_string() const + { + return get_version_string<T>(product_version_ms_, product_version_ls_); + } + +public: //Setters + //Sets if file is debug-built + void set_debug(bool debug); + //Sets if file is prerelease + void set_prerelease(bool prerelease); + //Sets if file is patched + void set_patched(bool patched); + //Sets if private build + void set_private_build(bool private_build); + //Sets if special build + void set_special_build(bool special_build); + //Sets if info inferred + void set_info_inferred(bool info_inferred); + //Sets flags (raw DWORD) + void set_file_flags(uint32_t file_flags); + + //Sets file version most significant DWORD + void set_file_version_ms(uint32_t file_version_ms); + //Sets file version least significant DWORD + void set_file_version_ls(uint32_t file_version_ls); + //Sets product version most significant DWORD + void set_product_version_ms(uint32_t product_version_ms); + //Sets product version least significant DWORD + void set_product_version_ls(uint32_t product_version_ls); + + //Sets file OS type (raw DWORD) + void set_file_os_raw(uint32_t file_os); + //Sets file OS type + void set_file_os(file_os_type file_os); + + //Sets file type (raw DWORD) + void set_file_type_raw(uint32_t file_type); + //Sets file type + void set_file_type(file_type file_type); + + //Sets file subtype (usually non-zero for drivers and fonts) + void set_file_subtype(uint32_t file_subtype); + + //Sets file date most significant DWORD + void set_file_date_ms(uint32_t file_date_ms); + //Sets file date least significant DWORD + void set_file_date_ls(uint32_t file_date_ls); + +private: + //Helper to convert version DWORDs to string + template<typename T> + static const std::basic_string<T> get_version_string(uint32_t ms, uint32_t ls) + { + std::basic_stringstream<T> ss; + ss << (ms >> 16) << static_cast<T>(L'.') + << (ms & 0xFFFF) << static_cast<T>(L'.') + << (ls >> 16) << static_cast<T>(L'.') + << (ls & 0xFFFF); + return ss.str(); + } + + //Helper to set file flag + void set_file_flag(uint32_t flag); + //Helper to clear file flag + void clear_file_flag(uint32_t flag); + //Helper to set or clear file flag + void set_file_flag(uint32_t flag, bool set_flag); + + uint32_t file_version_ms_, file_version_ls_, + product_version_ms_, product_version_ls_; + uint32_t file_flags_; + uint32_t file_os_; + uint32_t file_type_, file_subtype_; + uint32_t file_date_ms_, file_date_ls_; +}; +} diff --git a/pe_lib/message_table.cpp b/pe_lib/message_table.cpp new file mode 100644 index 0000000..1c71219 --- /dev/null +++ b/pe_lib/message_table.cpp @@ -0,0 +1,60 @@ +#include "message_table.h" +#include "utils.h" + +namespace pe_bliss +{ +//Default constructor +message_table_item::message_table_item() + :unicode_(false) +{} + +//Constructor from ANSI string +message_table_item::message_table_item(const std::string& str) + :unicode_(false), ansi_str_(str) +{ + pe_utils::strip_nullbytes(ansi_str_); +} + +//Constructor from UNICODE string +message_table_item::message_table_item(const std::wstring& str) + :unicode_(true), unicode_str_(str) +{ + pe_utils::strip_nullbytes(unicode_str_); +} + +//Returns true if contained string is unicode +bool message_table_item::is_unicode() const +{ + return unicode_; +} + +//Returns ANSI string +const std::string& message_table_item::get_ansi_string() const +{ + return ansi_str_; +} + +//Returns UNICODE string +const std::wstring& message_table_item::get_unicode_string() const +{ + return unicode_str_; +} + +//Sets ANSI string (clears UNICODE one) +void message_table_item::set_string(const std::string& str) +{ + ansi_str_ = str; + pe_utils::strip_nullbytes(ansi_str_); + unicode_str_.clear(); + unicode_ = false; +} + +//Sets UNICODE string (clears ANSI one) +void message_table_item::set_string(const std::wstring& str) +{ + unicode_str_ = str; + pe_utils::strip_nullbytes(unicode_str_); + ansi_str_.clear(); + unicode_ = true; +} +} diff --git a/pe_lib/message_table.h b/pe_lib/message_table.h new file mode 100644 index 0000000..13c3e25 --- /dev/null +++ b/pe_lib/message_table.h @@ -0,0 +1,35 @@ +#pragma once +#include <string> +#include <map> +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Structure representing message table string +class message_table_item +{ +public: + //Default constructor + message_table_item(); + //Constructors from ANSI and UNICODE strings + explicit message_table_item(const std::string& str); + explicit message_table_item(const std::wstring& str); + + //Returns true if string is UNICODE + bool is_unicode() const; + //Returns ANSI string + const std::string& get_ansi_string() const; + //Returns UNICODE string + const std::wstring& get_unicode_string() const; + +public: + //Sets ANSI or UNICODE string + void set_string(const std::string& str); + void set_string(const std::wstring& str); + +private: + bool unicode_; + std::string ansi_str_; + std::wstring unicode_str_; +}; +} diff --git a/pe_lib/pe_32_64.cpp b/pe_lib/pe_32_64.cpp index e9ab74e..b895f18 100644 --- a/pe_lib/pe_32_64.cpp +++ b/pe_lib/pe_32_64.cpp @@ -169,7 +169,7 @@ uint32_t pe<PEClassType>::strip_data_directories(uint32_t min_count, bool strip_ for(; i >= 0; i--) { //If directory exists, break - if(nt_headers_.OptionalHeader.DataDirectory[i].VirtualAddress && (i != image_directory_entry_iat || !strip_iat_directory)) + if(nt_headers_.OptionalHeader.DataDirectory[i].VirtualAddress && (static_cast<uint32_t>(i) != image_directory_entry_iat || !strip_iat_directory)) break; if(i <= static_cast<int>(min_count) - 2) diff --git a/pe_lib/pe_base.cpp b/pe_lib/pe_base.cpp index ee555e0..504b200 100644 --- a/pe_lib/pe_base.cpp +++ b/pe_lib/pe_base.cpp @@ -9,233 +9,101 @@ #include "pe_exception.h" #include "pe_base.h" -#ifndef PE_BLISS_WINDOWS -#include <iconv.h> -#endif - namespace pe_bliss { using namespace pe_win; -const double pe_base::log_2 = 1.44269504088896340736; //instead of using M_LOG2E - -//Destructor -pe_base::~pe_base() -{} - -//Section structure default constructor -pe_base::section::section() - :raw_size_aligned_(0), virtual_size_aligned_(0), old_size_(static_cast<size_t>(-1)) -{ - memset(&header_, 0, sizeof(image_section_header)); -} - -//Sets the name of section (8 characters maximum) -void pe_base::section::set_name(const std::string& name) -{ - memset(header_.Name, 0, sizeof(header_.Name)); - memcpy(header_.Name, name.c_str(), std::min<size_t>(name.length(), sizeof(header_.Name))); -} - -//Returns section name -const std::string pe_base::section::get_name() const -{ - char buf[9] = {0}; - memcpy(buf, header_.Name, 8); - return std::string(buf); -} - -//Set flag (attribute) of section -pe_base::section& pe_base::section::set_flag(uint32_t flag, bool setflag) -{ - if(setflag) - header_.Characteristics |= flag; - else - header_.Characteristics &= ~flag; - - return *this; -} - -//Sets "readable" attribute of section -pe_base::section& pe_base::section::readable(bool readable) -{ - return set_flag(image_scn_mem_read, readable); -} - -//Sets "writeable" attribute of section -pe_base::section& pe_base::section::writeable(bool writeable) -{ - return set_flag(image_scn_mem_write, writeable); -} - -//Sets "executable" attribute of section -pe_base::section& pe_base::section::executable(bool executable) -{ - return set_flag(image_scn_mem_execute, executable); -} - -//Sets "shared" attribute of section -pe_base::section& pe_base::section::shared(bool shared) -{ - return set_flag(image_scn_mem_shared, shared); -} - -//Sets "discardable" attribute of section -pe_base::section& pe_base::section::discardable(bool discardable) -{ - return set_flag(image_scn_mem_discardable, discardable); -} - -//Returns true if section is readable -bool pe_base::section::readable() const -{ - return (header_.Characteristics & image_scn_mem_read) != 0; -} - -//Returns true if section is writeable -bool pe_base::section::writeable() const -{ - return (header_.Characteristics & image_scn_mem_write) != 0; -} - -//Returns true if section is executable -bool pe_base::section::executable() const -{ - return (header_.Characteristics & image_scn_mem_execute) != 0; -} - -bool pe_base::section::shared() const -{ - return (header_.Characteristics & image_scn_mem_shared) != 0; -} - -bool pe_base::section::discardable() const -{ - return (header_.Characteristics & image_scn_mem_discardable) != 0; -} - -//Returns true if section has no RAW data -bool pe_base::section::empty() const -{ - if(old_size_ != static_cast<size_t>(-1)) //If virtual memory is mapped, check raw data length (old_size_) - return old_size_ == 0; - else - return raw_data_.empty(); -} - -//Returns raw section data from file image -std::string& pe_base::section::get_raw_data() -{ - unmap_virtual(); - return raw_data_; -} - -//Sets raw section data from file image -void pe_base::section::set_raw_data(const std::string& data) +//Constructor +pe_base::pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data) { - old_size_ = static_cast<size_t>(-1); - raw_data_ = data; -} - -//Returns raw section data from file image -const std::string& pe_base::section::get_raw_data() const -{ - unmap_virtual(); - return raw_data_; -} + props_ = props.duplicate().release(); -//Returns mapped virtual section data -const std::string& pe_base::section::get_virtual_data() const -{ - map_virtual(); - return raw_data_; -} - -//Returns mapped virtual section data -std::string& pe_base::section::get_virtual_data() -{ - map_virtual(); - return raw_data_; -} + //Save istream state + std::ios_base::iostate state = file.exceptions(); + std::streamoff old_offset = file.tellg(); -//Maps virtual section data -void pe_base::section::map_virtual() const -{ - if(old_size_ == static_cast<size_t>(-1) && virtual_size_aligned_ && virtual_size_aligned_ > raw_data_.length()) + try { - old_size_ = raw_data_.length(); - raw_data_.resize(virtual_size_aligned_, 0); + file.exceptions(std::ios::goodbit); + //Read DOS header, PE headers and section data + read_dos_header(file); + read_pe(file, read_debug_raw_data); } -} - -//Unmaps virtual section data -void pe_base::section::unmap_virtual() const -{ - if(old_size_ != static_cast<size_t>(-1)) + catch(const std::exception&) { - raw_data_.resize(old_size_, 0); - old_size_ = static_cast<size_t>(-1); + //If something went wrong, restore istream state + file.seekg(old_offset); + file.exceptions(state); + file.clear(); + //Rethrow + throw; } -} -//Returns section virtual size -uint32_t pe_base::section::get_virtual_size() const -{ - return header_.Misc.VirtualSize; + //Restore istream state + file.seekg(old_offset); + file.exceptions(state); + file.clear(); } -//Returns section virtual address -uint32_t pe_base::section::get_virtual_address() const +pe_base::pe_base(const pe_properties& props, uint32_t section_alignment, bool dll, uint16_t subsystem) { - return header_.VirtualAddress; -} + props_ = props.duplicate().release(); + props_->create_pe(section_alignment, subsystem); -//Returns size of section raw data -uint32_t pe_base::section::get_size_of_raw_data() const -{ - return header_.SizeOfRawData; -} + has_overlay_ = false; + memset(&dos_header_, 0, sizeof(dos_header_)); -//Returns pointer to raw section data in PE file -uint32_t pe_base::section::get_pointer_to_raw_data() const -{ - return header_.PointerToRawData; -} + dos_header_.e_magic = 0x5A4D; //"MZ" + //Magic numbers from MSVC++ build + dos_header_.e_maxalloc = 0xFFFF; + dos_header_.e_cblp = 0x90; + dos_header_.e_cp = 3; + dos_header_.e_cparhdr = 4; + dos_header_.e_sp = 0xB8; + dos_header_.e_lfarlc = 64; -//Returns section characteristics -uint32_t pe_base::section::get_characteristics() const -{ - return header_.Characteristics; -} + set_characteristics(image_file_executable_image | image_file_relocs_stripped); -//Sets size of raw section data -void pe_base::section::set_size_of_raw_data(uint32_t size_of_raw_data) -{ - header_.SizeOfRawData = size_of_raw_data; -} + if(get_pe_type() == pe_type_32) + set_characteristics_flags(image_file_32bit_machine); -//Sets pointer to section raw data -void pe_base::section::set_pointer_to_raw_data(uint32_t pointer_to_raw_data) -{ - header_.PointerToRawData = pointer_to_raw_data; + if(dll) + set_characteristics_flags(image_file_dll); + + set_subsystem_version(5, 1); //WinXP + set_os_version(5, 1); //WinXP } -//Sets section characteristics -void pe_base::section::set_characteristics(uint32_t characteristics) +pe_base::pe_base(const pe_base& pe) + :dos_header_(pe.dos_header_), + rich_overlay_(pe.rich_overlay_), + sections_(pe.sections_), + has_overlay_(pe.has_overlay_), + full_headers_data_(pe.full_headers_data_), + debug_data_(pe.debug_data_), + props_(0) { - header_.Characteristics = characteristics; + props_ = pe.props_->duplicate().release(); } -//Sets section virtual size -void pe_base::section::set_virtual_size(uint32_t virtual_size) +pe_base& pe_base::operator=(const pe_base& pe) { - header_.Misc.VirtualSize = virtual_size; + dos_header_ = pe.dos_header_; + rich_overlay_ = pe.rich_overlay_; + sections_ = pe.sections_; + has_overlay_ = pe.has_overlay_; + full_headers_data_ = pe.full_headers_data_; + debug_data_ = pe.debug_data_; + delete props_; + props_ = 0; + props_ = pe.props_->duplicate().release(); + + return *this; } -//Sets section virtual address -void pe_base::section::set_virtual_address(uint32_t virtual_address) +pe_base::~pe_base() { - header_.VirtualAddress = virtual_address; + delete props_; } //Returns dos header @@ -269,6 +137,12 @@ void pe_base::fill_stub_overlay(char c) rich_overlay_.assign(rich_overlay_.length(), c); } +//Sets stub MSVS overlay +void pe_base::set_stub_overlay(const std::string& data) +{ + rich_overlay_ = data; +} + //Returns stub overlay const std::string& pe_base::get_stub_overlay() const { @@ -282,14 +156,34 @@ void pe_base::realign_all_sections() realign_section(i); } +//Returns number of sections from PE header +uint16_t pe_base::get_number_of_sections() const +{ + return props_->get_number_of_sections(); +} + +//Updates number of sections in PE header +uint16_t pe_base::update_number_of_sections() +{ + uint16_t new_number = static_cast<uint16_t>(sections_.size()); + props_->set_number_of_sections(new_number); + return new_number; +} + +//Returns section alignment +uint32_t pe_base::get_section_alignment() const +{ + return props_->get_section_alignment(); +} + //Returns image sections list -pe_base::section_list& pe_base::get_image_sections() +section_list& pe_base::get_image_sections() { return sections_; } //Returns image sections list -const pe_base::section_list& pe_base::get_image_sections() const +const section_list& pe_base::get_image_sections() const { return sections_; } @@ -303,34 +197,39 @@ void pe_base::realign_section(uint32_t index) //Get section iterator section_list::iterator it = sections_.begin() + index; + section& s = *it; //Calculate, how many null bytes we have in the end of raw section data std::size_t strip = 0; for(std::size_t i = (*it).get_raw_data().length(); i >= 1; --i) { - if((*it).get_raw_data()[i - 1] == 0) + if(s.get_raw_data()[i - 1] == 0) strip++; else break; } - //Calculate aligned raw size of section - (*it).raw_size_aligned_ = static_cast<uint32_t>(align_up((*it).get_raw_data().length() - strip, get_file_alignment())); - if(it == sections_.end() - 1) //If we're realigning the last section { //We can strip ending null bytes - (*it).header_.SizeOfRawData = static_cast<uint32_t>((*it).get_raw_data().length() - strip); - (*it).get_raw_data().resize((*it).get_raw_data().length() - strip, 0); + s.set_size_of_raw_data(static_cast<uint32_t>(s.get_raw_data().length() - strip)); + s.get_raw_data().resize(s.get_raw_data().length() - strip, 0); } else { //Else just set size of raw data - (*it).header_.SizeOfRawData = (*it).raw_size_aligned_; - (*it).get_raw_data().resize((*it).raw_size_aligned_, 0); + uint32_t raw_size_aligned = s.get_aligned_raw_size(get_file_alignment()); + s.set_size_of_raw_data(raw_size_aligned); + s.get_raw_data().resize(raw_size_aligned, 0); } } +//Returns file alignment +uint32_t pe_base::get_file_alignment() const +{ + return props_->get_file_alignment(); +} + //Sets file alignment void pe_base::set_file_alignment(uint32_t alignment) { @@ -338,7 +237,7 @@ void pe_base::set_file_alignment(uint32_t alignment) if(alignment < minimum_file_alignment) throw pe_exception("File alignment can't be less than 512", pe_exception::incorrect_file_alignment); - if(!is_power_of_2(alignment)) + if(!pe_utils::is_power_of_2(alignment)) throw pe_exception("File alignment must be a power of 2", pe_exception::incorrect_file_alignment); if(alignment > get_section_alignment()) @@ -348,42 +247,86 @@ void pe_base::set_file_alignment(uint32_t alignment) set_file_alignment_unchecked(alignment); } +//Returns size of image +uint32_t pe_base::get_size_of_image() const +{ + return props_->get_size_of_image(); +} + +//Returns image entry point +uint32_t pe_base::get_ep() const +{ + return props_->get_ep(); +} + +//Sets image entry point (just a value of PE header) +void pe_base::set_ep(uint32_t new_ep) +{ + props_->set_ep(new_ep); +} + +//Returns number of RVA and sizes (number of DATA_DIRECTORY entries) +uint32_t pe_base::get_number_of_rvas_and_sizes() const +{ + return props_->get_number_of_rvas_and_sizes(); +} + +//Sets number of RVA and sizes (number of DATA_DIRECTORY entries) +void pe_base::set_number_of_rvas_and_sizes(uint32_t number) +{ + props_->set_number_of_rvas_and_sizes(number); +} + +//Returns PE characteristics +uint16_t pe_base::get_characteristics() const +{ + return props_->get_characteristics(); +} + +//Sets PE characteristics (a value inside header) +void pe_base::set_characteristics(uint16_t ch) +{ + props_->set_characteristics(ch); +} + //Returns section from RVA -pe_base::section& pe_base::section_from_rva(uint32_t rva) +section& pe_base::section_from_rva(uint32_t rva) { //Search for section for(section_list::iterator i = sections_.begin(); i != sections_.end(); ++i) { + section& s = *i; //Return section if found - if(rva >= (*i).header_.VirtualAddress && rva < (*i).header_.VirtualAddress + (*i).virtual_size_aligned_) - return *i; + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + return s; } throw pe_exception("No section found by presented address", pe_exception::no_section_found); } //Returns section from RVA -const pe_base::section& pe_base::section_from_rva(uint32_t rva) const +const section& pe_base::section_from_rva(uint32_t rva) const { //Search for section for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i) { + const section& s = *i; //Return section if found - if(rva >= (*i).header_.VirtualAddress && rva < (*i).header_.VirtualAddress + (*i).virtual_size_aligned_) - return *i; + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + return s; } throw pe_exception("No section found by presented address", pe_exception::no_section_found); } //Returns section from directory ID -pe_base::section& pe_base::section_from_directory(uint32_t directory_id) +section& pe_base::section_from_directory(uint32_t directory_id) { return section_from_rva(get_directory_rva(directory_id)); } //Returns section from directory ID -const pe_base::section& pe_base::section_from_directory(uint32_t directory_id) const +const section& pe_base::section_from_directory(uint32_t directory_id) const { return section_from_rva(get_directory_rva(directory_id)); } @@ -399,15 +342,16 @@ void pe_base::set_section_virtual_size(section& s, uint32_t vsize) //If we're setting virtual size to zero if(vsize == 0) { + //Check if section is empty + if(s.empty()) + throw pe_exception("Cannot set virtual size of empty section to zero", pe_exception::error_changing_section_virtual_size); + //Set virtual size equal to aligned size of raw data - s.virtual_size_aligned_ = align_up(s.header_.SizeOfRawData, get_section_alignment()); - s.header_.Misc.VirtualSize = s.header_.SizeOfRawData; + s.set_virtual_size(s.get_size_of_raw_data()); } else { - //Else set aligned virtual size - s.virtual_size_aligned_ = align_up(vsize, get_section_alignment()); - s.header_.Misc.VirtualSize = s.virtual_size_aligned_; + s.set_virtual_size(vsize); } //Update image size if we're changing virtual size for the last section of this PE @@ -423,13 +367,14 @@ bool pe_base::expand_section(section& s, uint32_t needed_rva, uint32_t needed_si //Check if we're changing the last section //Of course, we can change the section that's not bound to this PE file if(sections_.empty() || std::find_if(sections_.begin(), sections_.end() - 1, section_ptr_finder(s)) != sections_.end() - 1) - throw pe_exception("Can't expand any section, except last one", pe_exception::error_changing_section_virtual_size); + throw pe_exception("Can't expand any section, except last one", pe_exception::error_expanding_section); //Check if we should expand our section if(expand == expand_section_raw && section_data_length_from_rva(s, needed_rva, section_data_raw) < needed_size) { //Expand section raw data s.get_raw_data().resize(needed_rva - s.get_virtual_address() + needed_size); + recalculate_section_sizes(s, false); return true; } else if(expand == expand_section_virtual && section_data_length_from_rva(s, needed_rva, section_data_virtual) < needed_size) @@ -447,36 +392,73 @@ void pe_base::update_image_size() { //Write virtual size of image to headers if(!sections_.empty()) - set_size_of_image(sections_.back().header_.VirtualAddress + sections_.back().virtual_size_aligned_); + set_size_of_image(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment())); + else + set_size_of_image(get_size_of_headers()); +} + +//Returns checksum of PE file from header +uint32_t pe_base::get_checksum() const +{ + return props_->get_checksum(); +} + +//Sets checksum of PE file +void pe_base::set_checksum(uint32_t checksum) +{ + props_->set_checksum(checksum); +} + +//Returns timestamp of PE file from header +uint32_t pe_base::get_time_date_stamp() const +{ + return props_->get_time_date_stamp(); +} + +//Sets timestamp of PE file +void pe_base::set_time_date_stamp(uint32_t timestamp) +{ + props_->set_time_date_stamp(timestamp); +} + +//Returns Machine field value of PE file from header +uint16_t pe_base::get_machine() const +{ + return props_->get_machine(); +} + +//Sets Machine field value of PE file +void pe_base::set_machine(uint16_t machine) +{ + props_->set_machine(machine); } //Prepares section before attaching it void pe_base::prepare_section(section& s) { //Calculate its size of raw data - s.header_.SizeOfRawData = static_cast<uint32_t>(align_up(s.get_raw_data().length(), get_file_alignment())); - s.raw_size_aligned_ = s.header_.SizeOfRawData; + s.set_size_of_raw_data(static_cast<uint32_t>(pe_utils::align_up(s.get_raw_data().length(), get_file_alignment()))); //Check section virtual and raw size - if(!s.header_.SizeOfRawData && !s.header_.Misc.VirtualSize) + if(!s.get_size_of_raw_data() && !s.get_virtual_size()) throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes); //If section virtual size is zero - if(s.header_.Misc.VirtualSize == 0) + if(!s.get_virtual_size()) { - //Set its virtual size as aligned raw size - s.virtual_size_aligned_ = align_up(s.header_.SizeOfRawData, get_section_alignment()); - s.header_.Misc.VirtualSize = s.header_.SizeOfRawData; + s.set_virtual_size(s.get_size_of_raw_data()); } else { //Else calculate its virtual size - s.virtual_size_aligned_ = std::max<uint32_t>(align_up(s.header_.SizeOfRawData, get_file_alignment()), align_up(s.header_.Misc.VirtualSize, get_section_alignment())); + s.set_virtual_size( + std::max<uint32_t>(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()), + pe_utils::align_up(s.get_virtual_size(), get_section_alignment()))); } } //Adds section to image -pe_base::section& pe_base::add_section(section s) +section& pe_base::add_section(section s) { if(sections_.size() >= maximum_number_of_sections) throw pe_exception("Maximum number of sections has been reached", pe_exception::no_more_sections_can_be_added); @@ -487,19 +469,18 @@ pe_base::section& pe_base::add_section(section s) //Calculate section virtual address if(!sections_.empty()) { - s.header_.VirtualAddress = align_up(sections_.back().header_.VirtualAddress + sections_.back().virtual_size_aligned_, get_section_alignment()); + s.set_virtual_address(pe_utils::align_up(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment()), get_section_alignment())); //We should align last section raw size, if it wasn't aligned section& last = sections_.back(); - last.header_.SizeOfRawData = static_cast<uint32_t>(align_up(last.get_raw_data().length(), get_file_alignment())); - s.raw_size_aligned_ = s.header_.SizeOfRawData; + last.set_size_of_raw_data(static_cast<uint32_t>(pe_utils::align_up(last.get_raw_data().length(), get_file_alignment()))); } else { - s.header_.VirtualAddress = - s.header_.VirtualAddress == 0 - ? align_up(get_size_of_headers(), get_section_alignment()) - : align_up(s.header_.VirtualAddress, get_section_alignment()); + s.set_virtual_address( + s.get_virtual_address() == 0 + ? pe_utils::align_up(get_size_of_headers(), get_section_alignment()) + : pe_utils::align_up(s.get_virtual_address(), get_section_alignment())); } //Add section to the end of section list @@ -507,7 +488,7 @@ pe_base::section& pe_base::add_section(section s) //Set number of sections in PE header set_number_of_sections(static_cast<uint16_t>(sections_.size())); //Recalculate virtual size of image - set_size_of_image(get_size_of_image() + s.virtual_size_aligned_); + set_size_of_image(get_size_of_image() + s.get_aligned_virtual_size(get_section_alignment())); //Return last section return sections_.back(); } @@ -518,6 +499,50 @@ bool pe_base::section_attached(const section& s) const return sections_.end() != std::find_if(sections_.begin(), sections_.end(), section_ptr_finder(s)); } +//Returns true if directory exists +bool pe_base::directory_exists(uint32_t id) const +{ + return props_->directory_exists(id); +} + +//Removes directory +void pe_base::remove_directory(uint32_t id) +{ + props_->remove_directory(id); +} + +//Returns directory RVA +uint32_t pe_base::get_directory_rva(uint32_t id) const +{ + return props_->get_directory_rva(id); +} + +//Returns directory size +uint32_t pe_base::get_directory_size(uint32_t id) const +{ + return props_->get_directory_size(id); +} + +//Sets directory RVA (just a value of PE header, no moving occurs) +void pe_base::set_directory_rva(uint32_t id, uint32_t rva) +{ + return props_->set_directory_rva(id, rva); +} + +//Sets directory size (just a value of PE header, no moving occurs) +void pe_base::set_directory_size(uint32_t id, uint32_t size) +{ + return props_->set_directory_size(id, size); +} + +//Strips only zero DATA_DIRECTORY entries to count = min_count +//Returns resulting number of data directories +//strip_iat_directory - if true, even not empty IAT directory will be stripped +uint32_t pe_base::strip_data_directories(uint32_t min_count, bool strip_iat_directory) +{ + return props_->strip_data_directories(min_count, strip_iat_directory); +} + //Returns true if image has import directory bool pe_base::has_imports() const { @@ -594,23 +619,23 @@ bool pe_base::has_debug() const char* pe_base::section_data_from_rva(section& s, uint32_t rva) { //Check if RVA is inside section "s" - if(rva >= s.header_.VirtualAddress && rva < s.header_.VirtualAddress + s.virtual_size_aligned_) + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) { if(s.get_raw_data().empty()) throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty); - return &s.get_raw_data()[rva - s.header_.VirtualAddress]; + return &s.get_raw_data()[rva - s.get_virtual_address()]; } throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); } //Returns corresponding section data pointer from RVA inside section "s" (checks bounds) -const char* pe_base::section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype) +const char* pe_base::section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype) const { //Check if RVA is inside section "s" - if(rva >= s.header_.VirtualAddress && rva < s.header_.VirtualAddress + s.virtual_size_aligned_) - return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data().c_str()) + rva - s.header_.VirtualAddress; + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address(); throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); } @@ -623,7 +648,7 @@ uint32_t pe_base::section_data_length_from_rva(uint32_t rva, section_data_type d return static_cast<unsigned long>(full_headers_data_.length()); const section& s = section_from_rva(rva); - return static_cast<unsigned long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.virtual_size_aligned_); + return static_cast<unsigned long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())); } //Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32 @@ -646,9 +671,12 @@ uint32_t pe_base::section_data_length_from_rva(uint32_t rva, uint32_t rva_inside return static_cast<unsigned long>(full_headers_data_.length() - rva_inside); const section& s = section_from_rva(rva); + if(rva_inside < s.get_virtual_address()) + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); + //Calculate remaining length of section data from "rva" address - long length = static_cast<long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.virtual_size_aligned_) - + s.header_.VirtualAddress - rva_inside; + long length = static_cast<long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())) + + s.get_virtual_address() - rva_inside; if(length < 0) return 0; @@ -669,19 +697,19 @@ uint32_t pe_base::section_data_length_from_va(uint64_t va, uint64_t va_inside, s } //Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds) -uint32_t pe_base::section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype) +uint32_t pe_base::section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype) const { //Check rva_inside - if(rva_inside >= s.header_.VirtualAddress && rva_inside < s.header_.VirtualAddress + s.virtual_size_aligned_) + if(rva_inside >= s.get_virtual_address() && rva_inside < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) { //Calculate remaining length of section data from "rva" address - long length = static_cast<int>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.virtual_size_aligned_) - + s.header_.VirtualAddress - rva_inside; + int32_t length = static_cast<int32_t>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())) + + s.get_virtual_address() - rva_inside; if(length < 0) return 0; - return static_cast<unsigned long>(length); + return static_cast<uint32_t>(length); } throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); @@ -711,7 +739,7 @@ char* pe_base::section_data_from_rva(uint32_t rva, bool include_headers) if(s.get_raw_data().empty()) throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty); - return &s.get_raw_data()[rva - s.header_.VirtualAddress]; + return &s.get_raw_data()[rva - s.get_virtual_address()]; } //Returns corresponding section data pointer from RVA inside section @@ -722,304 +750,90 @@ const char* pe_base::section_data_from_rva(uint32_t rva, section_data_type datat return &full_headers_data_[rva]; const section& s = section_from_rva(rva); - return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data().c_str()) + rva - s.header_.VirtualAddress; + return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address(); } - -//STUB OVERLAY -//Default constructor -pe_base::rich_data::rich_data() - :number_(0), version_(0), times_(0) -{} - -//Who knows, what these fields mean... -uint32_t pe_base::rich_data::get_number() const +//Reads DOS headers from istream +void pe_base::read_dos_header(std::istream& file, image_dos_header& header) { - return number_; -} + //Check istream flags + if(file.bad() || file.eof()) + throw pe_exception("PE file stream is bad or closed.", pe_exception::bad_pe_file); -uint32_t pe_base::rich_data::get_version() const -{ - return version_; -} + //Read DOS header and check istream + file.read(reinterpret_cast<char*>(&header), sizeof(image_dos_header)); + if(file.bad() || file.eof()) + throw pe_exception("Unable to read IMAGE_DOS_HEADER", pe_exception::bad_dos_header); -uint32_t pe_base::rich_data::get_times() const -{ - return times_; + //Check DOS header magic + if(header.e_magic != 0x5a4d) //"MZ" + throw pe_exception("IMAGE_DOS_HEADER signature is incorrect", pe_exception::bad_dos_header); } -void pe_base::rich_data::set_number(uint32_t number) +//Reads DOS headers from istream +void pe_base::read_dos_header(std::istream& file) { - number_ = number; + read_dos_header(file, dos_header_); } -void pe_base::rich_data::set_version(uint32_t version) +//Reads PE image from istream +void pe_base::read_pe(std::istream& file, bool read_debug_raw_data) { - version_ = version; -} + //Get istream size + std::streamoff filesize = pe_utils::get_file_size(file); -void pe_base::rich_data::set_times(uint32_t times) -{ - times_ = times; -} + //Check if PE header is DWORD-aligned + if((dos_header_.e_lfanew % sizeof(uint32_t)) != 0) + throw pe_exception("PE header is not DWORD-aligned", pe_exception::bad_dos_header); -//Returns MSVC rich data -const pe_base::rich_data_list pe_base::get_rich_data() const -{ - //Returned value - rich_data_list ret; + //Seek to NT headers + file.seekg(dos_header_.e_lfanew); + if(file.bad() || file.fail()) + throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found); - //If there's no rich overlay, return empty vector - if(rich_overlay_.size() < sizeof(uint32_t)) - return ret; + //Read NT headers + file.read(get_nt_headers_ptr(), get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries); + if(file.bad() || file.eof()) + throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers); - //True if rich data was found - bool found = false; + //Check PE signature + if(get_pe_signature() != 0x4550) //"PE" + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); - //Rich overlay ID ("Rich" word) - static const uint32_t rich_overlay_id = 0x68636952; + //Check number of directories + if(get_number_of_rvas_and_sizes() > image_numberof_directory_entries) + set_number_of_rvas_and_sizes(image_numberof_directory_entries); - //Search for rich data overlay ID - const char* begin = &rich_overlay_[0]; - const char* end = begin + rich_overlay_.length(); - for(; begin != end; ++begin) + if(get_number_of_rvas_and_sizes() > 0) { - if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) - { - found = true; //We've found it! - break; - } + //Read data directory headers, if any + file.read(get_nt_headers_ptr() + (get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries), sizeof(image_data_directory) * get_number_of_rvas_and_sizes()); + if(file.bad() || file.eof()) + throw pe_exception("Error reading DATA_DIRECTORY headers", pe_exception::error_reading_data_directories); } - //If we found it - if(found) - { - //Check remaining length - if(static_cast<size_t>(end - begin) < sizeof(uint32_t)) - return ret; - - //The XOR key is after "Rich" word, we should get it - uint32_t xorkey = *reinterpret_cast<const uint32_t*>(begin + sizeof(uint32_t)); + //Check section number + //Images with zero section number accepted + if(get_number_of_sections() > maximum_number_of_sections) + throw pe_exception("Incorrect number of sections", pe_exception::section_number_incorrect); - //True if rich data was found - found = false; + //Check PE magic + if(get_magic() != get_needed_magic()) + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); - //Second search for signature "DanS" - begin = &rich_overlay_[0]; - for(; begin != end; ++begin) - { - if((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) == 0x536e6144) //"DanS" - { - found = true; - break; - } - } + //Check section alignment + if(!pe_utils::is_power_of_2(get_section_alignment())) + throw pe_exception("Incorrect section alignment", pe_exception::incorrect_section_alignment); - //If second signature is found - if(found) - { - begin += sizeof(uint32_t) * 3; - //List all rich data structures - while(begin < end) - { - begin += sizeof(uint32_t); - if(begin >= end) - break; + //Check file alignment + if(!pe_utils::is_power_of_2(get_file_alignment())) + throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); - //Check for rich overlay data end ("Rich" word reached) - if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) - break; - - //Create rich_data structure - rich_data data; - data.set_number((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) >> 16); - data.set_version((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) & 0xFFFF); - - begin += sizeof(uint32_t); - if(begin >= end) - break; - - data.set_times(*reinterpret_cast<const uint32_t*>(begin) ^ xorkey); - - //Save rich data structure - ret.push_back(data); - } - } - } - - //Return rich data structures list - return ret; -} - -//Rebuilds PE image headers -//If strip_dos_header is true, DOS headers partially will be used for PE headers -void pe_base::rebuild_pe(bool strip_dos_header, bool change_size_of_headers) -{ - //Set start of PE headers - dos_header_.e_lfanew = sizeof(image_dos_header) + static_cast<uint32_t>(rich_overlay_.size()); - - if(strip_dos_header) - { - //Set base of code as 8 * sizeof(WORD) - //Leave first 8 WORDs of DOS header untouched - set_base_of_code(8 * sizeof(uint16_t)); - //Strip stub overlay - strip_stub_overlay(); - } - - //Calculate pointer to section data - ptr_to_section_data_ = align_up((strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)) + get_sizeof_nt_header() + rich_overlay_.size() - - sizeof(image_data_directory) * (image_numberof_directory_entries - get_number_of_rvas_and_sizes()) - + sections_.size() * sizeof(image_section_header), get_file_alignment()); - - //Set size of headers and size of optional header - if(!sections_.empty() && change_size_of_headers) - set_size_of_headers(std::min<uint32_t>(static_cast<uint32_t>(ptr_to_section_data_), (*sections_.begin()).header_.VirtualAddress)); - - set_size_of_optional_header(static_cast<uint16_t>(get_sizeof_opt_headers() - sizeof(image_data_directory) * (image_numberof_directory_entries - get_number_of_rvas_and_sizes()))); - - //Recalculate pointer to raw data according to section list - for(section_list::iterator it = sections_.begin(); it != sections_.end(); ++it) - { - //Save section headers PointerToRawData - (*it).header_.PointerToRawData = static_cast<uint32_t>(ptr_to_section_data_); - ptr_to_section_data_ += (*it).raw_size_aligned_; - } -} - -//Rebuild PE image and write it to "out" ostream -//If strip_dos_header is true, DOS headers partially will be used for PE headers -void pe_base::rebuild_pe(std::ostream& out, bool strip_dos_header, bool change_size_of_headers) -{ - if(out.bad()) - throw pe_exception("Stream is bad", pe_exception::stream_is_bad); - - //Change ostream state - out.exceptions(std::ios::goodbit); - out.clear(); - - //Rebuild PE image headers - rebuild_pe(strip_dos_header, change_size_of_headers); - - //Write DOS header - out.write(reinterpret_cast<const char*>(&dos_header_), strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)); - //If we have rich overlay, write it too - if(rich_overlay_.size()) - out.write(rich_overlay_.data(), rich_overlay_.size()); - - //Write NT headers - out.write(get_nt_headers_ptr(), get_sizeof_nt_header() - sizeof(image_data_directory) * (image_numberof_directory_entries - get_number_of_rvas_and_sizes())); - - //Write section headers - for(section_list::iterator it = sections_.begin(); it != sections_.end(); ++it) - { - if(it == sections_.end() - 1) //If last section encountered - { - image_section_header header = (*it).header_; - header.SizeOfRawData = static_cast<uint32_t>((*it).get_raw_data().length()); //Set non-aligned actual data length for it - out.write(reinterpret_cast<const char*>(&header), sizeof(image_section_header)); - } - else - { - out.write(reinterpret_cast<const char*>(&(*it).header_), sizeof(image_section_header)); - } - } - - //Write section data finally - for(section_list::iterator it = sections_.begin(); it != sections_.end(); ++it) - { - //Get current write position of stream - std::streamoff wpos = out.tellp(); - //Fill unused overlay data between sections with null bytes - for(unsigned int i = 0; i < (*it).header_.PointerToRawData - wpos; i++) - out.put(0); - - //Write raw section data - out.write((*it).get_raw_data().data(), (*it).get_raw_data().length()); - } -} - -//Reads DOS headers from istream -void pe_base::read_dos_header(std::istream& file, image_dos_header& header) -{ - //Check istream flags - if(file.bad() || file.eof()) - throw pe_exception("PE file stream is bad or closed.", pe_exception::bad_pe_file); - - //Read DOS header and check istream - file.read(reinterpret_cast<char*>(&header), sizeof(image_dos_header)); - if(file.bad() || file.eof()) - throw pe_exception("Unable to read IMAGE_DOS_HEADER", pe_exception::bad_dos_header); - - //Check DOS header magic - if(header.e_magic != 0x5a4d) //"MZ" - throw pe_exception("IMAGE_DOS_HEADER signature is incorrect", pe_exception::bad_dos_header); -} - -//Reads DOS headers from istream -void pe_base::read_dos_header(std::istream& file) -{ - read_dos_header(file, dos_header_); -} - -//Reads PE image from istream -void pe_base::read_pe(std::istream& file, bool read_bound_import_raw_data, bool read_debug_raw_data) -{ - //Get istream size - std::streamoff filesize = get_file_size(file); - - //Check if PE header is DWORD-aligned - if((dos_header_.e_lfanew % sizeof(uint32_t)) != 0) - throw pe_exception("PE header is not DWORD-aligned", pe_exception::bad_dos_header); - - //Seek to NT headers - file.seekg(dos_header_.e_lfanew); - if(file.bad() || file.fail()) - throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found); - - //Read NT headers - file.read(get_nt_headers_ptr(), get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries); - if(file.bad() || file.eof()) - throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers); - - //Check PE signature - if(get_pe_signature() != 0x4550) //"PE" - throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); - - //Check number of directories - if(get_number_of_rvas_and_sizes() > image_numberof_directory_entries) - set_number_of_rvas_and_sizes(image_numberof_directory_entries); - - if(get_number_of_rvas_and_sizes() > 0) - { - //Read data directory headers, if any - file.read(get_nt_headers_ptr() + (get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries), sizeof(image_data_directory) * get_number_of_rvas_and_sizes()); - if(file.bad() || file.eof()) - throw pe_exception("Error reading DATA_DIRECTORY headers", pe_exception::error_reading_data_directories); - } - - //Check section number - //Images with zero section number accepted - if(get_number_of_sections() > maximum_number_of_sections) - throw pe_exception("Incorrect number of sections", pe_exception::section_number_incorrect); - - //Check PE magic - if(get_magic() != get_needed_magic()) - throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); - - //Check section alignment - if(!is_power_of_2(get_section_alignment())) - throw pe_exception("Incorrect section alignment", pe_exception::incorrect_section_alignment); - - //Check file alignment - if(!is_power_of_2(get_file_alignment())) - throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); - - if(get_file_alignment() != get_section_alignment() && (get_file_alignment() < minimum_file_alignment || get_file_alignment() > get_section_alignment())) - throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); + if(get_file_alignment() != get_section_alignment() && (get_file_alignment() < minimum_file_alignment || get_file_alignment() > get_section_alignment())) + throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); //Check size of image - if(align_up(get_size_of_image(), get_section_alignment()) == 0) + if(pe_utils::align_up(get_size_of_image(), get_section_alignment()) == 0) throw pe_exception("Incorrect size of image", pe_exception::incorrect_size_of_image); //Read rich data overlay / DOS stub (if any) @@ -1051,7 +865,7 @@ void pe_base::read_pe(std::istream& file, bool read_bound_import_raw_data, bool { section s; //Read section header - file.read(reinterpret_cast<char*>(&s.header_), sizeof(image_section_header)); + file.read(reinterpret_cast<char*>(&s.get_raw_header()), sizeof(image_section_header)); if(file.bad() || file.eof()) throw pe_exception("Error reading section header", pe_exception::error_reading_section_header); @@ -1059,67 +873,43 @@ void pe_base::read_pe(std::istream& file, bool read_bound_import_raw_data, bool std::streamoff next_sect = file.tellg(); //Check section virtual and raw sizes - if(!s.header_.SizeOfRawData && !s.header_.Misc.VirtualSize) + if(!s.get_size_of_raw_data() && !s.get_virtual_size()) throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes); //Check for adequate values of section fields - if(!is_sum_safe(s.header_.VirtualAddress, s.header_.Misc.VirtualSize) || s.header_.Misc.VirtualSize > two_gb - || !is_sum_safe(s.header_.PointerToRawData, s.header_.SizeOfRawData) || s.header_.SizeOfRawData > two_gb) + if(!pe_utils::is_sum_safe(s.get_virtual_address(), s.get_virtual_size()) || s.get_virtual_size() > pe_utils::two_gb + || !pe_utils::is_sum_safe(s.get_pointer_to_raw_data(), s.get_size_of_raw_data()) || s.get_size_of_raw_data() > pe_utils::two_gb) throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); - if(s.header_.SizeOfRawData != 0) + if(s.get_size_of_raw_data() != 0) { //If section has raw data //If section raw data size is greater than virtual, fix it - last_raw_size = s.header_.SizeOfRawData; - if(align_up(s.header_.SizeOfRawData, get_file_alignment()) > align_up(s.header_.Misc.VirtualSize, get_section_alignment())) - s.header_.SizeOfRawData = s.header_.Misc.VirtualSize; + last_raw_size = s.get_size_of_raw_data(); + if(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()) > pe_utils::align_up(s.get_virtual_size(), get_section_alignment())) + s.set_size_of_raw_data(s.get_virtual_size()); //Check virtual and raw section sizes and addresses - if(s.header_.VirtualAddress + align_up(s.header_.Misc.VirtualSize, get_section_alignment()) > align_up(get_size_of_image(), get_section_alignment()) + if(s.get_virtual_address() + pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment()) || - align_down(s.header_.PointerToRawData, get_file_alignment()) + s.header_.SizeOfRawData > static_cast<uint32_t>(filesize)) + pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment()) + s.get_size_of_raw_data() > static_cast<uint32_t>(filesize)) throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); //Seek to section raw data - file.seekg(align_down(s.header_.PointerToRawData, get_file_alignment())); + file.seekg(pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment())); if(file.bad() || file.fail()) throw pe_exception("Cannot reach section data", pe_exception::image_section_data_not_found); - if(s.header_.Misc.VirtualSize == 0) - { - //If section virtual size is zero - //Set aligned virtual size of section as aligned raw size - s.virtual_size_aligned_ = align_up(s.header_.SizeOfRawData, get_section_alignment()); - } - else - { - //If section virtual size is not zero - //Set aligned virtual size of section as aligned virtual size - s.virtual_size_aligned_ = align_up(s.header_.Misc.VirtualSize, get_section_alignment()); - } - - //Set aligned raw size of section - s.raw_size_aligned_ = align_up(s.header_.SizeOfRawData, get_file_alignment()); - //Read section raw data - s.get_raw_data().resize(s.header_.SizeOfRawData); - file.read(&s.get_raw_data()[0], s.header_.SizeOfRawData); + s.get_raw_data().resize(s.get_size_of_raw_data()); + file.read(&s.get_raw_data()[0], s.get_size_of_raw_data()); if(file.bad() || file.fail()) throw pe_exception("Error reading section data", pe_exception::image_section_data_not_found); } - else - { - //If section doesn't have raw data - //Set raw size to zero - s.raw_size_aligned_ = 0; - //Calculate aligned virtual size of section - s.virtual_size_aligned_ = align_up(s.header_.Misc.VirtualSize, get_section_alignment()); - } //Check virtual address and size of section - if(s.header_.VirtualAddress + s.virtual_size_aligned_ > align_up(get_size_of_image(), get_section_alignment())) + if(s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment())) throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); //Save section @@ -1130,7 +920,7 @@ void pe_base::read_pe(std::istream& file, bool read_bound_import_raw_data, bool } //Check size of headers: SizeOfHeaders can't be larger than first section VA - if(!sections_.empty() && get_size_of_headers() > sections_.front().header_.VirtualAddress) + if(!sections_.empty() && get_size_of_headers() > sections_.front().get_virtual_address()) throw pe_exception("Incorrect size of headers", pe_exception::incorrect_size_of_headers); //If image has more than two sections @@ -1139,38 +929,31 @@ void pe_base::read_pe(std::istream& file, bool read_bound_import_raw_data, bool //Check sections virtual sizes for(section_list::const_iterator i = sections_.begin() + 1; i != sections_.end(); ++i) { - if((*i).header_.VirtualAddress != (*(i - 1)).header_.VirtualAddress + (*(i - 1)).virtual_size_aligned_) + if((*i).get_virtual_address() != (*(i - 1)).get_virtual_address() + (*(i - 1)).get_aligned_virtual_size(get_section_alignment())) throw pe_exception("Section table is incorrect", pe_exception::image_section_table_incorrect); } } //Check if image has overlay in the end of file - has_overlay_ = !sections_.empty() && filesize > static_cast<std::streamoff>(sections_.back().header_.PointerToRawData + last_raw_size); + has_overlay_ = !sections_.empty() && filesize > static_cast<std::streamoff>(sections_.back().get_pointer_to_raw_data() + last_raw_size); - //If image has bound import - if(read_bound_import_raw_data && has_bound_import()) { - //RVA of IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT is actually RAW address - //So we need to read this RAW data - file.seekg(get_directory_rva(image_directory_entry_bound_import)); + //Additionally, read data from the beginning of istream to size of headers + file.seekg(0); + uint32_t size_of_headers = std::min<uint32_t>(get_size_of_headers(), static_cast<uint32_t>(filesize)); - try + if(!sections_.empty()) { - bound_import_data_.resize(get_directory_size(image_directory_entry_bound_import)); - file.read(&bound_import_data_[0], get_directory_size(image_directory_entry_bound_import)); - if(file.bad() || file.eof()) - bound_import_data_.resize(0); //Don't throw error here, we'll throw it at request of bound import info - } - catch(const std::bad_alloc&) //bad_alloc error - { - bound_import_data_.resize(0); //Don't throw error here, we'll throw it at request of bound import info + for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i) + { + if(!(*i).empty()) + { + size_of_headers = std::min<uint32_t>(get_size_of_headers(), (*i).get_pointer_to_raw_data()); + break; + } + } } - } - { - //Additionally, read data from the beginning of istream to size of headers - file.seekg(0); - uint32_t size_of_headers = std::min<uint32_t>(get_size_of_headers(), static_cast<uint32_t>(filesize)); full_headers_data_.resize(size_of_headers); file.read(&full_headers_data_[0], size_of_headers); if(file.bad() || file.eof()) @@ -1231,8 +1014,14 @@ void pe_base::read_pe(std::istream& file, bool read_bound_import_raw_data, bool } } +//Returns PE type of this image +pe_type pe_base::get_pe_type() const +{ + return props_->get_pe_type(); +} + //Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks) -pe_base::pe_type pe_base::get_pe_type(std::istream& file) +pe_type pe_base::get_pe_type(std::istream& file) { //Save state of the istream std::ios_base::iostate state = file.exceptions(); @@ -1283,92 +1072,6 @@ pe_base::pe_type pe_base::get_pe_type(std::istream& file) return nt_headers.OptionalHeader.Magic == image_nt_optional_hdr64_magic ? pe_type_64 : pe_type_32; } -//Returns stream size -std::streamoff pe_base::get_file_size(std::istream& file) -{ - //Get old istream offset - std::streamoff old_offset = file.tellg(); - file.seekg(0, std::ios::end); - std::streamoff filesize = file.tellg(); - //Set old istream offset - file.seekg(old_offset); - return filesize; -} - -//Calculate checksum of image -uint32_t pe_base::calculate_checksum(std::istream& file) -{ - //Save istream state - std::ios_base::iostate state = file.exceptions(); - std::streamoff old_offset = file.tellg(); - - //Checksum value - unsigned long long checksum = 0; - - try - { - image_dos_header header; - - file.exceptions(std::ios::goodbit); - - //Read DOS header - read_dos_header(file, header); - - //Calculate PE checksum - file.seekg(0); - unsigned long long top = 0xFFFFFFFF; - top++; - - //"CheckSum" field position in optional PE headers - it's always 64 for PE and PE+ - static const unsigned long checksum_pos_in_optional_headers = 64; - //Calculate real PE headers "CheckSum" field position - //Sum is safe here - unsigned long pe_checksum_pos = header.e_lfanew + sizeof(image_file_header) + sizeof(uint32_t) + checksum_pos_in_optional_headers; - - //Calculate checksum for each byte of file - std::streamoff filesize = get_file_size(file); - for(long long i = 0; i < filesize; i += 4) - { - unsigned long dw = 0; - - //Read DWORD from file - file.read(reinterpret_cast<char*>(&dw), sizeof(unsigned long)); - //Skip "CheckSum" DWORD - if(i == pe_checksum_pos) - continue; - - //Calculate checksum - checksum = (checksum & 0xffffffff) + dw + (checksum >> 32); - if(checksum > top) - checksum = (checksum & 0xffffffff) + (checksum >> 32); - } - - //Finish checksum - checksum = (checksum & 0xffff) + (checksum >> 16); - checksum = (checksum) + (checksum >> 16); - checksum = checksum & 0xffff; - - checksum += static_cast<unsigned long>(filesize); - } - catch(const std::exception&) - { - //If something went wrong, restore istream state - file.exceptions(state); - file.seekg(old_offset); - file.clear(); - //Rethrow - throw; - } - - //Restore istream state - file.exceptions(state); - file.seekg(old_offset); - file.clear(); - - //Return checksum - return static_cast<uint32_t>(checksum); -} - //Returns true if image has overlay data at the end of file bool pe_base::has_overlay() const { @@ -1393,16 +1096,64 @@ bool pe_base::check_characteristics_flag(uint16_t flag) const return (get_characteristics() & flag) ? true : false; } +//Returns subsystem value +uint16_t pe_base::get_subsystem() const +{ + return props_->get_subsystem(); +} + +//Sets subsystem value +void pe_base::set_subsystem(uint16_t subsystem) +{ + props_->set_subsystem(subsystem); +} + //Returns true if image has console subsystem bool pe_base::is_console() const { - return (get_subsystem() & image_subsystem_windows_cui) ? true : false; + return get_subsystem() == image_subsystem_windows_cui; } //Returns true if image has Windows GUI subsystem bool pe_base::is_gui() const { - return (get_subsystem() & image_subsystem_windows_gui) ? true : false; + return get_subsystem() == image_subsystem_windows_gui; +} + +//Sets required operation system version +void pe_base::set_os_version(uint16_t major, uint16_t minor) +{ + props_->set_os_version(major, minor); +} + +//Returns required operation system version (minor word) +uint16_t pe_base::get_minor_os_version() const +{ + return props_->get_minor_os_version(); +} + +//Returns required operation system version (major word) +uint16_t pe_base::get_major_os_version() const +{ + return props_->get_major_os_version(); +} + +//Sets required subsystem version +void pe_base::set_subsystem_version(uint16_t major, uint16_t minor) +{ + props_->set_subsystem_version(major, minor); +} + +//Returns required subsystem version (minor word) +uint16_t pe_base::get_minor_subsystem_version() const +{ + return props_->get_minor_subsystem_version(); +} + +//Returns required subsystem version (major word) +uint16_t pe_base::get_major_subsystem_version() const +{ + return props_->get_major_subsystem_version(); } //Returns corresponding section data pointer from VA inside section "s" for PE32 (checks bounds) @@ -1454,29 +1205,49 @@ const char* pe_base::section_data_from_va(uint64_t va, section_data_type datatyp } //Returns section from VA inside it for PE32 -pe_base::section& pe_base::section_from_va(uint32_t va) +section& pe_base::section_from_va(uint32_t va) { return section_from_rva(va_to_rva(va)); } //Returns section from VA inside it for PE32/PE64 -pe_base::section& pe_base::section_from_va(uint64_t va) +section& pe_base::section_from_va(uint64_t va) { return section_from_rva(va_to_rva(va)); } //Returns section from RVA inside it for PE32 -const pe_base::section& pe_base::section_from_va(uint32_t va) const +const section& pe_base::section_from_va(uint32_t va) const { return section_from_rva(va_to_rva(va)); } //Returns section from RVA inside it for PE32/PE64 -const pe_base::section& pe_base::section_from_va(uint64_t va) const +const section& pe_base::section_from_va(uint64_t va) const { return section_from_rva(va_to_rva(va)); } +uint32_t pe_base::va_to_rva(uint32_t va, bool bound_check) const +{ + return props_->va_to_rva(va, bound_check); +} + +uint32_t pe_base::va_to_rva(uint64_t va, bool bound_check) const +{ + return props_->va_to_rva(va, bound_check); +} + +uint32_t pe_base::rva_to_va_32(uint32_t rva) const +{ + return props_->rva_to_va_32(rva); +} + +uint64_t pe_base::rva_to_va_64(uint32_t rva) const +{ + return props_->rva_to_va_64(rva); +} + //Relative Virtual Address (RVA) to Virtual Address (VA) convertion for PE32 void pe_base::rva_to_va(uint32_t rva, uint32_t& va) const { @@ -1490,33 +1261,85 @@ void pe_base::rva_to_va(uint32_t rva, uint64_t& va) const } //Returns section from file offset (4gb max) -pe_base::section& pe_base::section_from_file_offset(uint32_t offset) +section& pe_base::section_from_file_offset(uint32_t offset) { return *file_offset_to_section(offset); } //Returns section from file offset (4gb max) -const pe_base::section& pe_base::section_from_file_offset(uint32_t offset) const +const section& pe_base::section_from_file_offset(uint32_t offset) const { return *file_offset_to_section(offset); } //Returns section and offset (raw data only) from its start from RVA -const std::pair<uint32_t, const pe_base::section*> pe_base::section_and_offset_from_rva(uint32_t rva) const +const std::pair<uint32_t, const section*> pe_base::section_and_offset_from_rva(uint32_t rva) const { const section& s = section_from_rva(rva); return std::make_pair(rva - s.get_virtual_address(), &s); } +//Returns DLL Characteristics +uint16_t pe_base::get_dll_characteristics() const +{ + return props_->get_dll_characteristics(); +} + +//Sets DLL Characteristics +void pe_base::set_dll_characteristics(uint16_t characteristics) +{ + props_->set_dll_characteristics(characteristics); +} + +//Returns size of headers +uint32_t pe_base::get_size_of_headers() const +{ + return props_->get_size_of_headers(); +} + +//Returns size of optional header +uint16_t pe_base::get_size_of_optional_header() const +{ + return props_->get_size_of_optional_header(); +} + +//Returns PE signature +uint32_t pe_base::get_pe_signature() const +{ + return props_->get_pe_signature(); +} + +//Returns magic value +uint32_t pe_base::get_magic() const +{ + return props_->get_magic(); +} + //Returns image base for PE32 void pe_base::get_image_base(uint32_t& base) const { base = get_image_base_32(); } +//Returns image base for PE32 and PE64 respectively +uint32_t pe_base::get_image_base_32() const +{ + return props_->get_image_base_32(); +} + +//Sets image base for PE32 and PE64 respectively +uint64_t pe_base::get_image_base_64() const +{ + return props_->get_image_base_64(); +} + //RVA to RAW file offset convertion (4gb max) uint32_t pe_base::rva_to_file_offset(uint32_t rva) const { + //Maybe, RVA is inside PE headers + if(rva < get_size_of_headers()) + return rva; + const section& s = section_from_rva(rva); return s.get_pointer_to_raw_data() + rva - s.get_virtual_address(); } @@ -1524,12 +1347,16 @@ uint32_t pe_base::rva_to_file_offset(uint32_t rva) const //RAW file offset to RVA convertion (4gb max) uint32_t pe_base::file_offset_to_rva(uint32_t offset) const { + //Maybe, offset is inside PE headers + if(offset < get_size_of_headers()) + return offset; + const section_list::const_iterator it = file_offset_to_section(offset); return offset - (*it).get_pointer_to_raw_data() + (*it).get_virtual_address(); } //RAW file offset to section convertion helper (4gb max) -pe_base::section_list::const_iterator pe_base::file_offset_to_section(uint32_t offset) const +section_list::const_iterator pe_base::file_offset_to_section(uint32_t offset) const { section_list::const_iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset)); if(it == sections_.end()) @@ -1539,26 +1366,15 @@ pe_base::section_list::const_iterator pe_base::file_offset_to_section(uint32_t o } //RAW file offset to section convertion helper (4gb max) -pe_base::section_list::iterator pe_base::file_offset_to_section(uint32_t offset) +section_list::iterator pe_base::file_offset_to_section(uint32_t offset) { section_list::iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset)); if(it == sections_.end()) throw pe_exception("No section found by presented file offset", pe_exception::no_section_found); - + return it; } -//Section by file offset finder helper (4gb max) -pe_base::section_by_raw_offset::section_by_raw_offset(uint32_t offset) - :offset_(offset) -{} - -bool pe_base::section_by_raw_offset::operator()(const section& s) const -{ - return (s.get_pointer_to_raw_data() <= offset_) - && (s.get_pointer_to_raw_data() + s.get_size_of_raw_data() > offset_); -} - //RVA from section raw data offset uint32_t pe_base::rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start) { @@ -1571,3743 +1387,151 @@ void pe_base::get_image_base(uint64_t& base) const base = get_image_base_64(); } -//Returns heap size commit for PE32 -void pe_base::get_heap_size_commit(uint32_t& size) const +//Sets new image base +void pe_base::set_image_base(uint32_t base) { - size = get_heap_size_commit_32(); + props_->set_image_base(base); } -//Returns heap size commit for PE32/PE64 -void pe_base::get_heap_size_commit(uint64_t& size) const -{ - size = get_heap_size_commit_64(); -} - -//Returns heap size reserve for PE32 -void pe_base::get_heap_size_reserve(uint32_t& size) const -{ - size = get_heap_size_reserve_32(); -} - -//Returns heap size reserve for PE32/PE64 -void pe_base::get_heap_size_reserve(uint64_t& size) const -{ - size = get_heap_size_reserve_64(); -} - -//Returns stack size commit for PE32 -void pe_base::get_stack_size_commit(uint32_t& size) const -{ - size = get_stack_size_commit_32(); -} - -//Returns stack size commit for PE32/PE64 -void pe_base::get_stack_size_commit(uint64_t& size) const -{ - size = get_stack_size_commit_64(); -} - -//Returns stack size reserve for PE32 -void pe_base::get_stack_size_reserve(uint32_t& size) const -{ - size = get_stack_size_reserve_32(); -} - -//Returns stack size reserve for PE32/PE64 -void pe_base::get_stack_size_reserve(uint64_t& size) const -{ - size = get_stack_size_reserve_64(); -} - - -//EXPORTS -//Default constructor -pe_base::exported_function::exported_function() - :ordinal_(0), rva_(0), has_name_(false), name_ordinal_(0), forward_(false) -{} - -//Returns ordinal of function (actually, ordinal = hint + ordinal base) -uint16_t pe_base::exported_function::get_ordinal() const -{ - return ordinal_; -} - -//Returns RVA of function -uint32_t pe_base::exported_function::get_rva() const -{ - return rva_; -} - -//Returns name of function -const std::string& pe_base::exported_function::get_name() const -{ - return name_; -} - -//Returns true if function has name and name ordinal -bool pe_base::exported_function::has_name() const -{ - return has_name_; -} - -//Returns name ordinal of function -uint16_t pe_base::exported_function::get_name_ordinal() const -{ - return name_ordinal_; -} - -//Returns true if function is forwarded to other library -bool pe_base::exported_function::is_forwarded() const -{ - return forward_; -} - -//Returns the name of forwarded function -const std::string& pe_base::exported_function::get_forwarded_name() const -{ - return forward_name_; -} - -//Sets ordinal of function -void pe_base::exported_function::set_ordinal(uint16_t ordinal) -{ - ordinal_ = ordinal; -} - -//Sets RVA of function -void pe_base::exported_function::set_rva(uint32_t rva) -{ - rva_ = rva; -} - -//Sets name of function (or clears it, if empty name is passed) -void pe_base::exported_function::set_name(const std::string& name) -{ - name_ = name; - has_name_ = !name.empty(); -} - -//Sets name ordinal -void pe_base::exported_function::set_name_ordinal(uint16_t name_ordinal) -{ - name_ordinal_ = name_ordinal; -} - -//Sets forwarded function name (or clears it, if empty name is passed) -void pe_base::exported_function::set_forwarded_name(const std::string& name) -{ - forward_name_ = name; - forward_ = !name.empty(); -} - -//Default constructor -pe_base::export_info::export_info() - :characteristics_(0), - timestamp_(0), - major_version_(0), - minor_version_(0), - ordinal_base_(0), - number_of_functions_(0), - number_of_names_(0), - address_of_functions_(0), - address_of_names_(0), - address_of_name_ordinals_(0) -{} - -//Returns characteristics -uint32_t pe_base::export_info::get_characteristics() const -{ - return characteristics_; -} - -//Returns timestamp -uint32_t pe_base::export_info::get_timestamp() const -{ - return timestamp_; -} - -//Returns major version -uint16_t pe_base::export_info::get_major_version() const -{ - return major_version_; -} - -//Returns minor version -uint16_t pe_base::export_info::get_minor_version() const -{ - return minor_version_; -} - -//Returns DLL name -const std::string& pe_base::export_info::get_name() const -{ - return name_; -} - -//Returns ordinal base -uint32_t pe_base::export_info::get_ordinal_base() const -{ - return ordinal_base_; -} - -//Returns number of functions -uint32_t pe_base::export_info::get_number_of_functions() const -{ - return number_of_functions_; -} - -//Returns number of function names -uint32_t pe_base::export_info::get_number_of_names() const -{ - return number_of_names_; -} - -//Returns RVA of function address table -uint32_t pe_base::export_info::get_rva_of_functions() const -{ - return address_of_functions_; -} - -//Returns RVA of function name address table -uint32_t pe_base::export_info::get_rva_of_names() const -{ - return address_of_names_; -} - -//Returns RVA of name ordinals table -uint32_t pe_base::export_info::get_rva_of_name_ordinals() const -{ - return address_of_name_ordinals_; -} - -//Sets characteristics -void pe_base::export_info::set_characteristics(uint32_t characteristics) -{ - characteristics_ = characteristics; -} - -//Sets timestamp -void pe_base::export_info::set_timestamp(uint32_t timestamp) -{ - timestamp_ = timestamp; -} - -//Sets major version -void pe_base::export_info::set_major_version(uint16_t major_version) -{ - major_version_ = major_version; -} - -//Sets minor version -void pe_base::export_info::set_minor_version(uint16_t minor_version) -{ - minor_version_ = minor_version; -} - -//Sets DLL name -void pe_base::export_info::set_name(const std::string& name) -{ - name_ = name; -} - -//Sets ordinal base -void pe_base::export_info::set_ordinal_base(uint32_t ordinal_base) -{ - ordinal_base_ = ordinal_base; -} - -//Sets number of functions -void pe_base::export_info::set_number_of_functions(uint32_t number_of_functions) -{ - number_of_functions_ = number_of_functions; -} - -//Sets number of function names -void pe_base::export_info::set_number_of_names(uint32_t number_of_names) -{ - number_of_names_ = number_of_names; -} - -//Sets RVA of function address table -void pe_base::export_info::set_rva_of_functions(uint32_t rva_of_functions) -{ - address_of_functions_ = rva_of_functions; -} - -//Sets RVA of function name address table -void pe_base::export_info::set_rva_of_names(uint32_t rva_of_names) -{ - address_of_names_ = rva_of_names; -} - -//Sets RVA of name ordinals table -void pe_base::export_info::set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals) -{ - address_of_name_ordinals_ = rva_of_name_ordinals; -} - -//Returns array of exported functions -const pe_base::exported_functions_list pe_base::get_exported_functions() const -{ - return get_exported_functions(0); -} - -//Returns array of exported functions and information about export -const pe_base::exported_functions_list pe_base::get_exported_functions(export_info& info) const -{ - return get_exported_functions(&info); -} - -//Returns array of exported functions and information about export (if info != 0) -const std::vector<pe_base::exported_function> pe_base::get_exported_functions(export_info* info) const -{ - //Returned exported functions info array - std::vector<exported_function> ret; - - if(has_exports()) - { - //Check the length in bytes of the section containing export directory - if(section_data_length_from_rva(get_directory_rva(image_directory_entry_export), get_directory_rva(image_directory_entry_export), section_data_virtual, true) < sizeof(image_export_directory)) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - image_export_directory exports = section_data_from_rva<image_export_directory>(get_directory_rva(image_directory_entry_export), section_data_virtual, true); - - unsigned long max_name_length; - - if(info) - { - //Save some export info data - info->set_characteristics(exports.Characteristics); - info->set_major_version(exports.MajorVersion); - info->set_minor_version(exports.MinorVersion); - - //Get byte count that we have for dll name - if((max_name_length = section_data_length_from_rva(exports.Name, exports.Name, section_data_virtual, true)) < 2) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - //Get dll name pointer - const char* dll_name = section_data_from_rva(exports.Name, section_data_virtual, true); - - //Check for null-termination - if(!is_null_terminated(dll_name, max_name_length)) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - //Save the rest of export information data - info->set_name(dll_name); - info->set_number_of_functions(exports.NumberOfFunctions); - info->set_number_of_names(exports.NumberOfNames); - info->set_ordinal_base(exports.Base); - info->set_rva_of_functions(exports.AddressOfFunctions); - info->set_rva_of_names(exports.AddressOfNames); - info->set_rva_of_name_ordinals(exports.AddressOfNameOrdinals); - info->set_timestamp(exports.TimeDateStamp); - } - - if(!exports.NumberOfFunctions) - return ret; - - //Check IMAGE_EXPORT_DIRECTORY fields - if(exports.NumberOfNames > exports.NumberOfFunctions) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - //Check some export directory fields - if((!exports.AddressOfNameOrdinals && exports.AddressOfNames) || - (exports.AddressOfNameOrdinals && !exports.AddressOfNames) || - !exports.AddressOfFunctions - || exports.NumberOfFunctions >= max_dword / sizeof(uint32_t) - || exports.NumberOfNames > max_dword / sizeof(uint32_t) - || !is_sum_safe(exports.AddressOfFunctions, exports.NumberOfFunctions * sizeof(uint32_t)) - || !is_sum_safe(exports.AddressOfNames, exports.NumberOfNames * sizeof(uint32_t)) - || !is_sum_safe(exports.AddressOfNameOrdinals, exports.NumberOfFunctions * sizeof(uint32_t)) - || !is_sum_safe(get_directory_rva(image_directory_entry_export), get_directory_size(image_directory_entry_export))) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - //Check if it is enough bytes to hold AddressOfFunctions table - if(section_data_length_from_rva(exports.AddressOfFunctions, exports.AddressOfFunctions, section_data_virtual, true) < exports.NumberOfFunctions * sizeof(uint32_t)) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - if(exports.AddressOfNames) - { - //Check if it is enough bytes to hold name and ordinal tables - if(section_data_length_from_rva(exports.AddressOfNameOrdinals, exports.AddressOfNameOrdinals, section_data_virtual, true) < exports.NumberOfNames * sizeof(uint16_t)) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - if(section_data_length_from_rva(exports.AddressOfNames, exports.AddressOfNames, section_data_virtual, true) < exports.NumberOfNames * sizeof(uint32_t)) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - } - - for(uint32_t ordinal = 0; ordinal < exports.NumberOfFunctions; ordinal++) - { - //Get function address - //Sum and multiplication are safe (checked above) - uint32_t rva = section_data_from_rva<uint32_t>(exports.AddressOfFunctions + ordinal * sizeof(uint32_t), section_data_virtual, true); - - //If we have a skip - if(!rva) - continue; - - exported_function func; - func.set_rva(rva); - - if(!is_sum_safe(exports.Base, ordinal) || exports.Base + ordinal > max_word) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - func.set_ordinal(static_cast<uint16_t>(ordinal + exports.Base)); - - //Scan for function name ordinal - for(uint32_t i = 0; i < exports.NumberOfNames; i++) - { - uint16_t ordinal2 = section_data_from_rva<uint16_t>(exports.AddressOfNameOrdinals + i * sizeof(uint16_t), section_data_virtual, true); - - //If function has name (and name ordinal) - if(ordinal == ordinal2) - { - //Get function name - //Sum and multiplication are safe (checked above) - uint32_t function_name_rva = section_data_from_rva<uint32_t>(exports.AddressOfNames + i * sizeof(uint32_t), section_data_virtual, true); - - //Get byte count that we have for function name - if((max_name_length = section_data_length_from_rva(function_name_rva, function_name_rva, section_data_virtual, true)) < 2) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - //Get function name pointer - const char* func_name = section_data_from_rva(function_name_rva, section_data_virtual, true); - - //Check for null-termination - if(!is_null_terminated(func_name, max_name_length)) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - //Save function info - func.set_name(func_name); - func.set_name_ordinal(ordinal2); - - //If the function is just a redirect, save its name - if(rva >= get_directory_rva(image_directory_entry_export) + sizeof(image_directory_entry_export) && - rva < get_directory_rva(image_directory_entry_export) + get_directory_size(image_directory_entry_export)) - { - if((max_name_length = section_data_length_from_rva(rva, rva, section_data_virtual, true)) < 2) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - //Get forwarded function name pointer - const char* forwarded_func_name = section_data_from_rva(rva, section_data_virtual, true); - - //Check for null-termination - if(!is_null_terminated(forwarded_func_name, max_name_length)) - throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); - - //Set the name of forwarded function - func.set_forwarded_name(forwarded_func_name); - } - - break; - } - } - - //Add function info to output array - ret.push_back(func); - } - } - - return ret; -} - -//Helper export functions -//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> -const std::pair<uint16_t, uint16_t> pe_base::get_export_ordinal_limits(const exported_functions_list& exports) -{ - if(exports.empty()) - return std::make_pair(0, 0); - - uint16_t max_ordinal = 0; //Maximum ordinal number - uint16_t ordinal_base = max_word; //Minimum ordinal value - for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) - { - const exported_function& func = (*it); - - //Calculate maximum and minimum ordinal numbers - max_ordinal = std::max<uint16_t>(max_ordinal, func.get_ordinal()); - ordinal_base = std::min<uint16_t>(ordinal_base, func.get_ordinal()); - } - - return std::make_pair(ordinal_base, max_ordinal); -} - -//Checks if exported function name already exists -bool pe_base::exported_name_exists(const std::string& function_name, const exported_functions_list& exports) -{ - for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) - { - if((*it).has_name() && (*it).get_name() == function_name) - return true; - } - - return false; -} - -//Checks if exported function name already exists -bool pe_base::exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports) -{ - for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) - { - if((*it).get_ordinal() == ordinal) - return true; - } - - return false; -} - -//Helper: sorts exported function list by ordinals -bool pe_base::ordinal_sorter::operator()(const exported_function& func1, const exported_function& func2) const -{ - return func1.get_ordinal() < func2.get_ordinal(); -} - -//Export directory rebuilder -//info - export information -//exported_functions_list - list of exported functions -//exports_section - section where export directory will be placed (must be attached to PE image) -//offset_from_section_start - offset from exports_section raw data start -//save_to_pe_headers - if true, new export directory information will be saved to PE image headers -//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped -//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently -//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure -//Returns new export directory information -//exported_functions_list is copied intentionally to be sorted by ordinal values later -//Name ordinals in exported function doesn't matter, they will be recalculated -const pe_base::image_directory pe_base::rebuild_exports(const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) -{ - //Check that exports_section is attached to this PE image - if(!section_attached(exports_section)) - throw pe_exception("Exports section must be attached to PE file", pe_exception::section_is_not_attached); - - //Needed space for strings - uint32_t needed_size_for_strings = static_cast<uint32_t>(info.get_name().length() + 1); - uint32_t number_of_names = 0; //Number of named functions - uint32_t max_ordinal = 0; //Maximum ordinal number - uint32_t ordinal_base = static_cast<uint32_t>(-1); //Minimum ordinal value - - if(exports.empty()) - ordinal_base = info.get_ordinal_base(); - - uint32_t needed_size_for_function_names = 0; //Needed space for function name strings - uint32_t needed_size_for_function_forwards = 0; //Needed space for function forwards names - - //List all exported functions - //Calculate needed size for function list - { - //Also check that there're no duplicate names and ordinals - std::set<std::string> used_function_names; - std::set<uint16_t> used_function_ordinals; - - for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) - { - const exported_function& func = (*it); - //Calculate maximum and minimum ordinal numbers - max_ordinal = std::max<uint32_t>(max_ordinal, func.get_ordinal()); - ordinal_base = std::min<uint32_t>(ordinal_base, func.get_ordinal()); - - //Check if ordinal is unique - if(!used_function_ordinals.insert(func.get_ordinal()).second) - throw pe_exception("Duplicate exported function ordinal", pe_exception::duplicate_exported_function_ordinal); - - if(func.has_name()) - { - //If function is named - ++number_of_names; - needed_size_for_function_names += static_cast<uint32_t>(func.get_name().length() + 1); - - //Check if it's name and name ordinal are unique - if(!used_function_names.insert(func.get_name()).second) - throw pe_exception("Duplicate exported function name", pe_exception::duplicate_exported_function_name); - } - - //If function is forwarded to another DLL - if(func.is_forwarded()) - needed_size_for_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); - } - } - - //Sort functions by ordinal value - std::sort(exports.begin(), exports.end(), ordinal_sorter()); - - //Calculate needed space for different things... - needed_size_for_strings += needed_size_for_function_names; - needed_size_for_strings += needed_size_for_function_forwards; - uint32_t needed_size_for_function_name_ordinals = number_of_names * sizeof(uint16_t); - uint32_t needed_size_for_function_name_rvas = number_of_names * sizeof(uint32_t); - uint32_t needed_size_for_function_addresses = (max_ordinal - ordinal_base + 1) * sizeof(uint32_t); - - uint32_t needed_size = sizeof(image_export_directory) + sizeof(uint32_t); //Calculate needed size for export tables and strings - //sizeof(IMAGE_EXPORT_DIRECTORY) = export directory header - //sizeof(uint32_t) = for DWORD alignment - - //Total needed space... - needed_size += needed_size_for_function_name_ordinals; //For list of names ordinals - needed_size += needed_size_for_function_addresses; //For function RVAs - needed_size += needed_size_for_strings; //For all strings - needed_size += needed_size_for_function_name_rvas; //For function name strings RVAs - - //Check if exports_section is last one. If it's not, check if there's enough place for exports data - if(&exports_section != &*(sections_.end() - 1) && - (exports_section.empty() || align_up(exports_section.get_size_of_raw_data(), get_file_alignment()) < needed_size + offset_from_section_start)) - throw pe_exception("Insufficient space for export directory", pe_exception::insufficient_space); - - std::string& raw_data = exports_section.get_raw_data(); - - //This will be done only is exports_section is the last section of image or for section with unaligned raw length of data - if(raw_data.length() < needed_size + offset_from_section_start) - raw_data.resize(needed_size + offset_from_section_start); //Expand section raw data - - //Export directory header will be placed first - uint32_t directory_pos = align_up(offset_from_section_start, sizeof(uint32_t)); - //Library name will be placed after it - uint32_t current_pos_of_function_names = static_cast<uint32_t>(info.get_name().length() + 1 + directory_pos + sizeof(image_export_directory)); - //Next - function names - uint32_t current_pos_of_function_name_ordinals = current_pos_of_function_names + needed_size_for_function_names; - //Next - function name ordinals - uint32_t current_pos_of_function_forwards = current_pos_of_function_name_ordinals + needed_size_for_function_name_ordinals; - //Finally - function addresses - uint32_t current_pos_of_function_addresses = current_pos_of_function_forwards + needed_size_for_function_forwards; - //Next - function names RVAs - uint32_t current_pos_of_function_names_rvas = current_pos_of_function_addresses + needed_size_for_function_addresses; - - { - //Create export directory and fill it - image_export_directory dir = {0}; - dir.Characteristics = info.get_characteristics(); - dir.MajorVersion = info.get_major_version(); - dir.MinorVersion = info.get_minor_version(); - dir.TimeDateStamp = info.get_timestamp(); - dir.NumberOfFunctions = max_ordinal - ordinal_base + 1; - dir.NumberOfNames = number_of_names; - dir.Base = ordinal_base; - dir.AddressOfFunctions = rva_from_section_offset(exports_section, current_pos_of_function_addresses); - dir.AddressOfNameOrdinals = rva_from_section_offset(exports_section, current_pos_of_function_name_ordinals); - dir.AddressOfNames = rva_from_section_offset(exports_section, current_pos_of_function_names_rvas); - dir.Name = rva_from_section_offset(exports_section, directory_pos + sizeof(image_export_directory)); - - //Save it - memcpy(&raw_data[directory_pos], &dir, sizeof(dir)); - } - - //Sve library name - memcpy(&raw_data[directory_pos + sizeof(image_export_directory)], info.get_name().c_str(), info.get_name().length() + 1); - - //A map to sort function names alphabetically - typedef std::map<std::string, uint16_t> funclist; //function name; function name ordinal - funclist funcs; - - uint32_t last_ordinal = ordinal_base; - //Enumerate all exported functions - for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) - { - const exported_function& func = (*it); - - //If we're skipping some ordinals... - if(func.get_ordinal() > last_ordinal) - { - //Fill this function RVAs data with zeros - uint32_t len = sizeof(uint32_t) * (func.get_ordinal() - last_ordinal - 1); - if(len) - { - memset(&raw_data[current_pos_of_function_addresses], 0, len); - current_pos_of_function_addresses += len; - } - - //Save last encountered ordinal - last_ordinal = func.get_ordinal(); - } - - //If function is named, save its name ordinal and name in sorted alphabetically order - if(func.has_name()) - funcs.insert(std::make_pair(func.get_name(), static_cast<uint16_t>(func.get_ordinal() - ordinal_base))); //Calculate name ordinal - - //If function is forwarded to another DLL - if(func.is_forwarded()) - { - //Write its forwarded name and its RVA - uint32_t function_rva = rva_from_section_offset(exports_section, current_pos_of_function_forwards); - memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); - current_pos_of_function_addresses += sizeof(function_rva); - - memcpy(&raw_data[current_pos_of_function_forwards], func.get_forwarded_name().c_str(), func.get_forwarded_name().length() + 1); - current_pos_of_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); - } - else - { - //Write actual function RVA - uint32_t function_rva = func.get_rva(); - memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); - current_pos_of_function_addresses += sizeof(function_rva); - } - } - - //Enumerate sorted function names - for(funclist::const_iterator it = funcs.begin(); it != funcs.end(); ++it) - { - //Save function name RVA - uint32_t function_name_rva = rva_from_section_offset(exports_section, current_pos_of_function_names); - memcpy(&raw_data[current_pos_of_function_names_rvas], &function_name_rva, sizeof(function_name_rva)); - current_pos_of_function_names_rvas += sizeof(function_name_rva); - - //Save function name - memcpy(&raw_data[current_pos_of_function_names], (*it).first.c_str(), (*it).first.length() + 1); - current_pos_of_function_names += static_cast<uint32_t>((*it).first.length() + 1); - - //Save function name ordinal - uint16_t name_ordinal = (*it).second; - memcpy(&raw_data[current_pos_of_function_name_ordinals], &name_ordinal, sizeof(name_ordinal)); - current_pos_of_function_name_ordinals += sizeof(name_ordinal); - } - - //Adjust section raw and virtual sizes - recalculate_section_sizes(exports_section, auto_strip_last_section); - - image_directory ret(rva_from_section_offset(exports_section, offset_from_section_start), needed_size); - - //If auto-rewrite of PE headers is required - if(save_to_pe_header) - { - set_directory_rva(image_directory_entry_export, ret.get_rva()); - set_directory_size(image_directory_entry_export, ret.get_size()); - } - - return ret; -} - - -//IMPORTS -//Default constructor -//If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset -//to new value after import rebuilding -//If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero -//IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable -//to be able to modify IAT thunks -pe_base::import_rebuilder_settings::import_rebuilder_settings(bool set_to_pe_headers, bool auto_zero_directory_entry_iat) - :offset_from_section_start_(0), - build_original_iat_(true), - save_iat_and_original_iat_rvas_(true), - fill_missing_original_iats_(false), - set_to_pe_headers_(set_to_pe_headers), - zero_directory_entry_iat_(auto_zero_directory_entry_iat), - rewrite_iat_and_original_iat_contents_(false), - auto_strip_last_section_(true) -{} - -//Returns offset from section start where import directory data will be placed -uint32_t pe_base::import_rebuilder_settings::get_offset_from_section_start() const -{ - return offset_from_section_start_; -} - -//Returns true if Original import address table (IAT) will be rebuilt -bool pe_base::import_rebuilder_settings::build_original_iat() const -{ - return build_original_iat_; -} - -//Returns true if Original import address and import address tables will not be rebuilt, -//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero -bool pe_base::import_rebuilder_settings::save_iat_and_original_iat_rvas() const -{ - return save_iat_and_original_iat_rvas_; -} - -//Returns true if Original import address and import address tables contents will be rewritten -//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero -//and save_iat_and_original_iat_rvas is true -bool pe_base::import_rebuilder_settings::rewrite_iat_and_original_iat_contents() const -{ - return rewrite_iat_and_original_iat_contents_; -} - -//Returns true if original missing IATs will be rebuilt -//(only if IATs are saved) -bool pe_base::import_rebuilder_settings::fill_missing_original_iats() const -{ - return fill_missing_original_iats_; -} - -//Returns true if PE headers should be updated automatically after rebuilding of imports -bool pe_base::import_rebuilder_settings::auto_set_to_pe_headers() const -{ - return set_to_pe_headers_; -} - -//Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true -bool pe_base::import_rebuilder_settings::zero_directory_entry_iat() const -{ - return zero_directory_entry_iat_; -} - -//Returns true if the last section should be stripped automatically, if imports are inside it -bool pe_base::import_rebuilder_settings::auto_strip_last_section_enabled() const -{ - return auto_strip_last_section_; -} - -//Sets offset from section start where import directory data will be placed -void pe_base::import_rebuilder_settings::set_offset_from_section_start(uint32_t offset) -{ - offset_from_section_start_ = offset; -} - -//Sets if Original import address table (IAT) will be rebuilt -void pe_base::import_rebuilder_settings::build_original_iat(bool enable) -{ - build_original_iat_ = enable; -} - -//Sets if Original import address and import address tables will not be rebuilt, -//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero -void pe_base::import_rebuilder_settings::save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents) -{ - save_iat_and_original_iat_rvas_ = enable; - if(save_iat_and_original_iat_rvas_) - rewrite_iat_and_original_iat_contents_ = enable_rewrite_iat_and_original_iat_contents; - else - rewrite_iat_and_original_iat_contents_ = false; -} - -//Sets if original missing IATs will be rebuilt -//(only if IATs are saved) -void pe_base::import_rebuilder_settings::fill_missing_original_iats(bool enable) -{ - fill_missing_original_iats_ = enable; -} - -//Sets if PE headers should be updated automatically after rebuilding of imports -void pe_base::import_rebuilder_settings::auto_set_to_pe_headers(bool enable) -{ - set_to_pe_headers_ = enable; -} - -//Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true -void pe_base::import_rebuilder_settings::zero_directory_entry_iat(bool enable) -{ - zero_directory_entry_iat_ = enable; -} - -//Sets if the last section should be stripped automatically, if imports are inside it, default true -void pe_base::import_rebuilder_settings::enable_auto_strip_last_section(bool enable) -{ - auto_strip_last_section_ = enable; -} - -//Default constructor -pe_base::imported_function::imported_function() - :hint_(0), ordinal_(0), iat_va_(0) -{} - -//Returns name of function -const std::string& pe_base::imported_function::get_name() const -{ - return name_; -} - -//Returns true if imported function has name (and hint) -bool pe_base::imported_function::has_name() const -{ - return !name_.empty(); -} - -//Returns hint -uint16_t pe_base::imported_function::get_hint() const -{ - return hint_; -} - -//Returns ordinal of function -uint16_t pe_base::imported_function::get_ordinal() const -{ - return ordinal_; -} - -//Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) -uint64_t pe_base::imported_function::get_iat_va() const -{ - return iat_va_; -} - -//Sets name of function -void pe_base::imported_function::set_name(const std::string& name) -{ - name_ = name; -} - -//Sets hint -void pe_base::imported_function::set_hint(uint16_t hint) -{ - hint_ = hint; -} - -//Sets ordinal -void pe_base::imported_function::set_ordinal(uint16_t ordinal) -{ - ordinal_ = ordinal; -} - -//Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) -void pe_base::imported_function::set_iat_va(uint64_t va) -{ - iat_va_ = va; -} - -//Default constructor -pe_base::import_library::import_library() - :rva_to_iat_(0), rva_to_original_iat_(0), timestamp_(0) -{} - -//Returns name of library -const std::string& pe_base::import_library::get_name() const -{ - return name_; -} - -//Returns RVA to Import Address Table (IAT) -uint32_t pe_base::import_library::get_rva_to_iat() const -{ - return rva_to_iat_; -} - -//Returns RVA to Original Import Address Table (Original IAT) -uint32_t pe_base::import_library::get_rva_to_original_iat() const -{ - return rva_to_original_iat_; -} - -//Returns timestamp -uint32_t pe_base::import_library::get_timestamp() const -{ - return timestamp_; -} - -//Sets name of library -void pe_base::import_library::set_name(const std::string& name) -{ - name_ = name; -} - -//Sets RVA to Import Address Table (IAT) -void pe_base::import_library::set_rva_to_iat(uint32_t rva_to_iat) -{ - rva_to_iat_ = rva_to_iat; -} - -//Sets RVA to Original Import Address Table (Original IAT) -void pe_base::import_library::set_rva_to_original_iat(uint32_t rva_to_original_iat) -{ - rva_to_original_iat_ = rva_to_original_iat; -} - -//Sets timestamp -void pe_base::import_library::set_timestamp(uint32_t timestamp) -{ - timestamp_ = timestamp; -} - -//Returns imported functions list -const pe_base::import_library::imported_list& pe_base::import_library::get_imported_functions() const -{ - return imports_; -} - -//Adds imported function -void pe_base::import_library::add_import(const imported_function& func) -{ - imports_.push_back(func); -} - -//Clears imported functions list -void pe_base::import_library::clear_imports() -{ - imports_.clear(); -} - -//RELOCATIONS -//Default constructor -pe_base::relocation_entry::relocation_entry() - :rva_(0), type_(0) -{} - -//Constructor from relocation item (WORD) -pe_base::relocation_entry::relocation_entry(uint16_t relocation_value) - :rva_(relocation_value & ((1 << 12) - 1)), type_(relocation_value >> 12) -{} - -//Constructor from relative rva and relocation type -pe_base::relocation_entry::relocation_entry(uint16_t rrva, uint16_t type) - :rva_(rrva), type_(type) -{} - -//Returns RVA of relocation -uint16_t pe_base::relocation_entry::get_rva() const -{ - return rva_; -} - -//Returns type of relocation -uint16_t pe_base::relocation_entry::get_type() const -{ - return type_; -} - -//Sets RVA of relocation -void pe_base::relocation_entry::set_rva(uint16_t rva) -{ - rva_ = rva; -} - -//Sets type of relocation -void pe_base::relocation_entry::set_type(uint16_t type) -{ - type_ = type; -} - -//Returns relocation item (rrva + type) -uint16_t pe_base::relocation_entry::get_item() const -{ - return rva_ | (type_ << 12); -} - -//Sets relocation item (rrva + type) -void pe_base::relocation_entry::set_item(uint16_t item) -{ - rva_ = item & ((1 << 12) - 1); - type_ = item >> 12; -} - -//Returns relocation list -const pe_base::relocation_table::relocation_list& pe_base::relocation_table::get_relocations() const -{ - return relocations_; -} - -//Adds relocation to table -void pe_base::relocation_table::add_relocation(const relocation_entry& entry) -{ - relocations_.push_back(entry); -} - -//Default constructor -pe_base::relocation_table::relocation_table() - :rva_(0) -{} - -//Constructor from RVA of relocation table -pe_base::relocation_table::relocation_table(uint32_t rva) - :rva_(rva) -{} - -//Returns RVA of block -uint32_t pe_base::relocation_table::get_rva() const -{ - return rva_; -} - -//Sets RVA of block -void pe_base::relocation_table::set_rva(uint32_t rva) -{ - rva_ = rva; -} - -//Returns changeable relocation list -pe_base::relocation_table::relocation_list& pe_base::relocation_table::get_relocations() -{ - return relocations_; -} - -//Get relocation list of pe file, supports one-word sized relocations only -//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed -const pe_base::relocation_table_list pe_base::get_relocations(bool list_absolute_entries) const -{ - relocation_table_list ret; - - //If image does not have relocations - if(!has_reloc()) - return ret; - - //Check the length in bytes of the section containing relocation directory - if(section_data_length_from_rva(get_directory_rva(image_directory_entry_basereloc), get_directory_rva(image_directory_entry_basereloc), section_data_virtual, true) < sizeof(image_base_relocation)) - throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); - - unsigned long current_pos = get_directory_rva(image_directory_entry_basereloc); - //First IMAGE_BASE_RELOCATION table - image_base_relocation reloc_table = section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); - - if(reloc_table.SizeOfBlock % 2) - throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); - - unsigned long reloc_size = get_directory_size(image_directory_entry_basereloc); - unsigned long read_size = 0; - - //reloc_table.VirtualAddress is not checked (not so important) - while(reloc_table.SizeOfBlock && read_size < reloc_size) - { - //Create relocation table - relocation_table table; - //Save RVA - table.set_rva(reloc_table.VirtualAddress); - - if(!is_sum_safe(current_pos, reloc_table.SizeOfBlock)) - throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); - - //List all relocations - for(unsigned long i = sizeof(image_base_relocation); i < reloc_table.SizeOfBlock; i += sizeof(uint16_t)) - { - relocation_entry entry(section_data_from_rva<uint16_t>(current_pos + i, section_data_virtual, true)); - if(list_absolute_entries || entry.get_type() != image_rel_based_absolute) - table.add_relocation(entry); - } - - //Save table - ret.push_back(table); - - //Go to next relocation block - if(!is_sum_safe(current_pos, reloc_table.SizeOfBlock)) - throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); - - current_pos += reloc_table.SizeOfBlock; - read_size += reloc_table.SizeOfBlock; - reloc_table = section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); - } - - return ret; -} - -//Simple relocations rebuilder -//To keep PE file working, don't remove any of existing relocations in -//relocation_table_list returned by a call to get_relocations() function -//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped -//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated -//If save_to_pe_header is true, PE header will be modified automatically -const pe_base::image_directory pe_base::rebuild_relocations(const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) -{ - //Check that reloc_section is attached to this PE image - if(!section_attached(reloc_section)) - throw pe_exception("Relocations section must be attached to PE file", pe_exception::section_is_not_attached); - - uint32_t current_reloc_data_pos = align_up(offset_from_section_start, sizeof(uint32_t)); - - uint32_t needed_size = current_reloc_data_pos - offset_from_section_start; //Calculate needed size for relocation tables - uint32_t size_delta = needed_size; - - uint32_t start_reloc_pos = current_reloc_data_pos; - - //Enumerate relocation tables - for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) - { - needed_size += static_cast<uint32_t>((*it).get_relocations().size() * sizeof(uint16_t) /* relocations */ + sizeof(image_base_relocation) /* table header */); - //End of each table will be DWORD-aligned - if((start_reloc_pos + needed_size - size_delta) % sizeof(uint32_t)) - needed_size += sizeof(uint16_t); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation - } - - //Check if reloc_section is last one. If it's not, check if there's enough place for relocations data - if(&reloc_section != &*(sections_.end() - 1) && - (reloc_section.empty() || align_up(reloc_section.get_size_of_raw_data(), get_file_alignment()) < needed_size + offset_from_section_start)) - throw pe_exception("Insufficient space for relocations directory", pe_exception::insufficient_space); - - std::string& raw_data = reloc_section.get_raw_data(); - - //This will be done only is reloc_section is the last section of image or for section with unaligned raw length of data - if(raw_data.length() < needed_size + offset_from_section_start) - raw_data.resize(needed_size + offset_from_section_start); //Expand section raw data - - //Enumerate relocation tables - for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) - { - //Create relocation table header - image_base_relocation reloc; - reloc.VirtualAddress = (*it).get_rva(); - const relocation_table::relocation_list& reloc_list = (*it).get_relocations(); - reloc.SizeOfBlock = static_cast<uint32_t>(sizeof(image_base_relocation) + sizeof(uint16_t) * reloc_list.size()); - if((reloc_list.size() * sizeof(uint16_t)) % sizeof(uint32_t)) //If we must align end of relocation table - reloc.SizeOfBlock += sizeof(uint16_t); - - memcpy(&raw_data[current_reloc_data_pos], &reloc, sizeof(reloc)); - current_reloc_data_pos += sizeof(reloc); - - //Enumerate relocations in table - for(relocation_table::relocation_list::const_iterator r = reloc_list.begin(); r != reloc_list.end(); ++r) - { - //Save relocations - uint16_t reloc_value = (*r).get_item(); - memcpy(&raw_data[current_reloc_data_pos], &reloc_value, sizeof(reloc_value)); - current_reloc_data_pos += sizeof(reloc_value); - } - - if(current_reloc_data_pos % sizeof(uint32_t)) //If end of table is not DWORD-aligned - { - memset(&raw_data[current_reloc_data_pos], 0, sizeof(uint16_t)); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation - current_reloc_data_pos += sizeof(uint16_t); - } - } - - image_directory ret(rva_from_section_offset(reloc_section, start_reloc_pos), needed_size - size_delta); - - //Adjust section raw and virtual sizes - recalculate_section_sizes(reloc_section, auto_strip_last_section); - - //If auto-rewrite of PE headers is required - if(save_to_pe_header) - { - set_directory_rva(image_directory_entry_basereloc, ret.get_rva()); - set_directory_size(image_directory_entry_basereloc, ret.get_size()); - - clear_characteristics_flags(image_file_relocs_stripped); - set_dll_characteristics(get_dll_characteristics() | image_dllcharacteristics_dynamic_base); - } - - return ret; -} - - -//TLS -//Default constructor -pe_base::tls_info::tls_info() - :start_rva_(0), end_rva_(0), index_rva_(0), callbacks_rva_(0), - size_of_zero_fill_(0), characteristics_(0) -{} - -//Returns start RVA of TLS raw data -uint32_t pe_base::tls_info::get_raw_data_start_rva() const -{ - return start_rva_; -} - -//Returns end RVA of TLS raw data -uint32_t pe_base::tls_info::get_raw_data_end_rva() const -{ - return end_rva_; -} - -//Returns TLS index RVA -uint32_t pe_base::tls_info::get_index_rva() const -{ - return index_rva_; -} - -//Returns TLS callbacks RVA -uint32_t pe_base::tls_info::get_callbacks_rva() const -{ - return callbacks_rva_; -} - -//Returns size of zero fill -uint32_t pe_base::tls_info::get_size_of_zero_fill() const -{ - return size_of_zero_fill_; -} - -//Returns characteristics -uint32_t pe_base::tls_info::get_characteristics() const -{ - return characteristics_; -} - -//Returns raw TLS data -const std::string& pe_base::tls_info::get_raw_data() const -{ - return raw_data_; -} - -//Returns TLS callbacks addresses -const pe_base::tls_info::tls_callback_list& pe_base::tls_info::get_tls_callbacks() const -{ - return callbacks_; -} - -//Returns TLS callbacks addresses -pe_base::tls_info::tls_callback_list& pe_base::tls_info::get_tls_callbacks() -{ - return callbacks_; -} - -//Adds TLS callback -void pe_base::tls_info::add_tls_callback(uint32_t rva) -{ - callbacks_.push_back(rva); -} - -//Clears TLS callbacks list -void pe_base::tls_info::clear_tls_callbacks() -{ - callbacks_.clear(); -} - -//Recalculates end address of raw TLS data -void pe_base::tls_info::recalc_raw_data_end_rva() -{ - end_rva_ = static_cast<uint32_t>(start_rva_ + raw_data_.length()); -} - -//Sets start RVA of TLS raw data -void pe_base::tls_info::set_raw_data_start_rva(uint32_t rva) -{ - start_rva_ = rva; -} - -//Sets end RVA of TLS raw data -void pe_base::tls_info::set_raw_data_end_rva(uint32_t rva) -{ - end_rva_ = rva; -} - -//Sets TLS index RVA -void pe_base::tls_info::set_index_rva(uint32_t rva) -{ - index_rva_ = rva; -} - -//Sets TLS callbacks RVA -void pe_base::tls_info::set_callbacks_rva(uint32_t rva) -{ - callbacks_rva_ = rva; -} - -//Sets size of zero fill -void pe_base::tls_info::set_size_of_zero_fill(uint32_t size) -{ - size_of_zero_fill_ = size; -} - -//Sets characteristics -void pe_base::tls_info::set_characteristics(uint32_t characteristics) -{ - characteristics_ = characteristics; -} - -//Sets raw TLS data -void pe_base::tls_info::set_raw_data(const std::string& data) -{ - raw_data_ = data; -} - -//IMAGE CONFIG -//Default constructor -pe_base::image_config_info::image_config_info() - :time_stamp_(0), - major_version_(0), minor_version_(0), - global_flags_clear_(0), global_flags_set_(0), - critical_section_default_timeout_(0), - decommit_free_block_threshold_(0), decommit_total_free_threshold_(0), - lock_prefix_table_va_(0), - max_allocation_size_(0), - virtual_memory_threshold_(0), - process_affinity_mask_(0), - process_heap_flags_(0), - service_pack_version_(0), - edit_list_va_(0), - security_cookie_va_(0), - se_handler_table_va_(0), - se_handler_count_(0) -{} - -//Constructors from PE structures -template<typename ConfigStructure> -pe_base::image_config_info::image_config_info(const ConfigStructure& info) - :time_stamp_(info.TimeDateStamp), - major_version_(info.MajorVersion), minor_version_(info.MinorVersion), - global_flags_clear_(info.GlobalFlagsClear), global_flags_set_(info.GlobalFlagsSet), - critical_section_default_timeout_(info.CriticalSectionDefaultTimeout), - decommit_free_block_threshold_(info.DeCommitFreeBlockThreshold), decommit_total_free_threshold_(info.DeCommitTotalFreeThreshold), - lock_prefix_table_va_(info.LockPrefixTable), - max_allocation_size_(info.MaximumAllocationSize), - virtual_memory_threshold_(info.VirtualMemoryThreshold), - process_affinity_mask_(info.ProcessAffinityMask), - process_heap_flags_(info.ProcessHeapFlags), - service_pack_version_(info.CSDVersion), - edit_list_va_(info.EditList), - security_cookie_va_(info.SecurityCookie), - se_handler_table_va_(info.SEHandlerTable), - se_handler_count_(info.SEHandlerCount) -{} - -//Instantiate template constructor with needed structures -template pe_base::image_config_info::image_config_info(const image_load_config_directory32& info); -template pe_base::image_config_info::image_config_info(const image_load_config_directory64& info); - -//Returns the date and time stamp value -uint32_t pe_base::image_config_info::get_time_stamp() const -{ - return time_stamp_; -} - -//Returns major version number -uint16_t pe_base::image_config_info::get_major_version() const -{ - return major_version_; -} - -//Returns minor version number -uint16_t pe_base::image_config_info::get_minor_version() const -{ - return minor_version_; -} - -//Returns clear global flags -uint32_t pe_base::image_config_info::get_global_flags_clear() const -{ - return global_flags_clear_; -} - -//Returns set global flags -uint32_t pe_base::image_config_info::get_global_flags_set() const -{ - return global_flags_set_; -} - -//Returns critical section default timeout -uint32_t pe_base::image_config_info::get_critical_section_default_timeout() const -{ - return critical_section_default_timeout_; -} - -//Get the size of the minimum block that -//must be freed before it is freed (de-committed), in bytes -uint64_t pe_base::image_config_info::get_decommit_free_block_threshold() const -{ - return decommit_free_block_threshold_; -} - -//Returns the size of the minimum total memory -//that must be freed in the process heap before it is freed (de-committed), in bytes -uint64_t pe_base::image_config_info::get_decommit_total_free_threshold() const -{ - return decommit_total_free_threshold_; -} - -//Returns VA of a list of addresses where the LOCK prefix is used -uint64_t pe_base::image_config_info::get_lock_prefix_table_va() const -{ - return lock_prefix_table_va_; -} - -//Returns the maximum allocation size, in bytes -uint64_t pe_base::image_config_info::get_max_allocation_size() const -{ - return max_allocation_size_; -} - -//Returns the maximum block size that can be allocated from heap segments, in bytes -uint64_t pe_base::image_config_info::get_virtual_memory_threshold() const -{ - return virtual_memory_threshold_; -} - -//Returns process affinity mask -uint64_t pe_base::image_config_info::get_process_affinity_mask() const -{ - return process_affinity_mask_; -} - -//Returns process heap flags -uint32_t pe_base::image_config_info::get_process_heap_flags() const -{ - return process_heap_flags_; -} - -//Returns service pack version (CSDVersion) -uint16_t pe_base::image_config_info::get_service_pack_version() const -{ - return service_pack_version_; -} - -//Returns VA of edit list (reserved by system) -uint64_t pe_base::image_config_info::get_edit_list_va() const -{ - return edit_list_va_; -} - -//Returns a pointer to a cookie that is used by Visual C++ or GS implementation -uint64_t pe_base::image_config_info::get_security_cookie_va() const -{ - return security_cookie_va_; -} - -//Returns VA of the sorted table of RVAs of each valid, unique handler in the image -uint64_t pe_base::image_config_info::get_se_handler_table_va() const -{ - return se_handler_table_va_; -} - -//Returns the count of unique handlers in the table -uint64_t pe_base::image_config_info::get_se_handler_count() const -{ - return se_handler_count_; -} - -//Returns SE Handler RVA list -const pe_base::image_config_info::se_handler_list& pe_base::image_config_info::get_se_handler_rvas() const -{ - return se_handlers_; -} - -//Returns Lock Prefix RVA list -const pe_base::image_config_info::lock_prefix_rva_list& pe_base::image_config_info::get_lock_prefix_rvas() const -{ - return lock_prefixes_; -} - -//Adds SE Handler RVA to list -void pe_base::image_config_info::add_se_handler_rva(uint32_t rva) -{ - se_handlers_.push_back(rva); -} - -//Clears SE Handler list -void pe_base::image_config_info::clear_se_handler_list() -{ - se_handlers_.clear(); -} - -//Adds Lock Prefix RVA to list -void pe_base::image_config_info::add_lock_prefix_rva(uint32_t rva) -{ - lock_prefixes_.push_back(rva); -} - -//Clears Lock Prefix list -void pe_base::image_config_info::clear_lock_prefix_list() -{ - lock_prefixes_.clear(); -} - -//Sets the date and time stamp value -void pe_base::image_config_info::set_time_stamp(uint32_t time_stamp) -{ - time_stamp_ = time_stamp; -} - -//Sets major version number -void pe_base::image_config_info::set_major_version(uint16_t major_version) -{ - major_version_ = major_version; -} - -//Sets minor version number -void pe_base::image_config_info::set_minor_version(uint16_t minor_version) -{ - minor_version_ = minor_version; -} - -//Sets clear global flags -void pe_base::image_config_info::set_global_flags_clear(uint32_t global_flags_clear) -{ - global_flags_clear_ = global_flags_clear; -} - -//Sets set global flags -void pe_base::image_config_info::set_global_flags_set(uint32_t global_flags_set) -{ - global_flags_set_ = global_flags_set; -} - -//Sets critical section default timeout -void pe_base::image_config_info::set_critical_section_default_timeout(uint32_t critical_section_default_timeout) -{ - critical_section_default_timeout_ = critical_section_default_timeout; -} - -//Sets the size of the minimum block that -//must be freed before it is freed (de-committed), in bytes -void pe_base::image_config_info::set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold) -{ - decommit_free_block_threshold_ = decommit_free_block_threshold; -} - -//Sets the size of the minimum total memory -//that must be freed in the process heap before it is freed (de-committed), in bytes -void pe_base::image_config_info::set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold) -{ - decommit_total_free_threshold_ = decommit_total_free_threshold; -} - -//Sets VA of a list of addresses where the LOCK prefix is used -//If you rebuild this list, VA will be re-assigned automatically -void pe_base::image_config_info::set_lock_prefix_table_va(uint64_t lock_prefix_table_va) -{ - lock_prefix_table_va_ = lock_prefix_table_va; -} - -//Sets the maximum allocation size, in bytes -void pe_base::image_config_info::set_max_allocation_size(uint64_t max_allocation_size) -{ - max_allocation_size_ = max_allocation_size; -} - -//Sets the maximum block size that can be allocated from heap segments, in bytes -void pe_base::image_config_info::set_virtual_memory_threshold(uint64_t virtual_memory_threshold) -{ - virtual_memory_threshold_ = virtual_memory_threshold; -} - -//Sets process affinity mask -void pe_base::image_config_info::set_process_affinity_mask(uint64_t process_affinity_mask) -{ - process_affinity_mask_ = process_affinity_mask; -} - -//Sets process heap flags -void pe_base::image_config_info::set_process_heap_flags(uint32_t process_heap_flags) -{ - process_heap_flags_ = process_heap_flags; -} - -//Sets service pack version (CSDVersion) -void pe_base::image_config_info::set_service_pack_version(uint16_t service_pack_version) -{ - service_pack_version_ = service_pack_version; -} - -//Sets VA of edit list (reserved by system) -void pe_base::image_config_info::set_edit_list_va(uint64_t edit_list_va) -{ - edit_list_va_ = edit_list_va; -} - -//Sets a pointer to a cookie that is used by Visual C++ or GS implementation -void pe_base::image_config_info::set_security_cookie_va(uint64_t security_cookie_va) -{ - security_cookie_va_ = security_cookie_va; -} - -//Sets VA of the sorted table of RVAs of each valid, unique handler in the image -//If you rebuild this list, VA will be re-assigned automatically -void pe_base::image_config_info::set_se_handler_table_va(uint64_t se_handler_table_va) -{ - se_handler_table_va_ = se_handler_table_va; -} - -//Returns SE Handler RVA list -pe_base::image_config_info::se_handler_list& pe_base::image_config_info::get_se_handler_rvas() -{ - return se_handlers_; -} - -//Returns Lock Prefix RVA list -pe_base::image_config_info::lock_prefix_rva_list& pe_base::image_config_info::get_lock_prefix_rvas() -{ - return lock_prefixes_; -} - -//BOUND IMPORT -//Default constructor -pe_base::bound_import_ref::bound_import_ref() - :timestamp_(0) -{} - -//Constructor from data -pe_base::bound_import_ref::bound_import_ref(const std::string& module_name, uint32_t timestamp) - :module_name_(module_name), timestamp_(timestamp) -{} - -//Returns imported module name -const std::string& pe_base::bound_import_ref::get_module_name() const -{ - return module_name_; -} - -//Returns bound import date and time stamp -uint32_t pe_base::bound_import_ref::get_timestamp() const -{ - return timestamp_; -} - -//Default constructor -pe_base::bound_import::bound_import() - :timestamp_(0) -{} - -//Constructor from data -pe_base::bound_import::bound_import(const std::string& module_name, uint32_t timestamp) - :module_name_(module_name), timestamp_(timestamp) -{} - -//Returns imported module name -const std::string& pe_base::bound_import::get_module_name() const -{ - return module_name_; -} - -//Returns bound import date and time stamp -uint32_t pe_base::bound_import::get_timestamp() const -{ - return timestamp_; -} - -//Returns bound references cound -size_t pe_base::bound_import::get_module_ref_count() const -{ - return refs_.size(); -} - -//Returns module references -const pe_base::bound_import::ref_list& pe_base::bound_import::get_module_ref_list() const -{ - return refs_; -} - -//Adds module reference -void pe_base::bound_import::add_module_ref(const bound_import_ref& ref) -{ - refs_.push_back(ref); -} - -//Clears module references list -void pe_base::bound_import::clear_module_refs() -{ - refs_.clear(); -} - -//Returns module references -pe_base::bound_import::ref_list& pe_base::bound_import::get_module_ref_list() -{ - return refs_; -} - -const pe_base::bound_import_module_list pe_base::get_bound_import_module_list() const -{ - //Returned bound import modules list - bound_import_module_list ret; - - //If image has no bound imports - if(!has_bound_import()) - return ret; - - //Check read in "read_pe" function raw bound import data size - if(bound_import_data_.size() < sizeof(image_bound_import_descriptor)) - throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); - - //current bound_import_data_ in-string position - unsigned long current_pos = 0; - //first bound import descriptor - //so, we're working with raw data here, no section helpers available - const image_bound_import_descriptor* descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data_[current_pos]); - - //Enumerate until zero - while(descriptor->OffsetModuleName) - { - //Check module name offset - if(descriptor->OffsetModuleName >= bound_import_data_.size()) - throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); - - //Check module name for null-termination - if(!is_null_terminated(&bound_import_data_[descriptor->OffsetModuleName], bound_import_data_.size() - descriptor->OffsetModuleName)) - throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); - - //Create bound import descriptor structure - bound_import elem(&bound_import_data_[descriptor->OffsetModuleName], descriptor->TimeDateStamp); - - //Check DWORDs - if(descriptor->NumberOfModuleForwarderRefs >= max_dword / sizeof(image_bound_forwarder_ref) - || !is_sum_safe(current_pos, 2 /* this descriptor and the next one */ * sizeof(image_bound_import_descriptor) + descriptor->NumberOfModuleForwarderRefs * sizeof(image_bound_forwarder_ref))) - throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); - - //Move after current descriptor - current_pos += sizeof(image_bound_import_descriptor); - - //Enumerate referenced bound import descriptors - for(unsigned long i = 0; i != descriptor->NumberOfModuleForwarderRefs; ++i) - { - //They're just after parent descriptor - //Check size of structure - if(current_pos + sizeof(image_bound_forwarder_ref) > bound_import_data_.size()) - throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); - - //Get IMAGE_BOUND_FORWARDER_REF pointer - const image_bound_forwarder_ref* ref_descriptor = reinterpret_cast<const image_bound_forwarder_ref*>(&bound_import_data_[current_pos]); - - //Check referenced module name - if(ref_descriptor->OffsetModuleName >= bound_import_data_.size()) - throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); - - //And its null-termination - if(!is_null_terminated(&bound_import_data_[ref_descriptor->OffsetModuleName], bound_import_data_.size() - ref_descriptor->OffsetModuleName)) - throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); - - //Add referenced module to current bound import structure - elem.add_module_ref(bound_import_ref(&bound_import_data_[ref_descriptor->OffsetModuleName], ref_descriptor->TimeDateStamp)); - - //Move after referenced bound import descriptor - current_pos += sizeof(image_bound_forwarder_ref); - } - - //Check structure size - if(current_pos + sizeof(image_bound_import_descriptor) > bound_import_data_.size()) - throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); - - //Move to next bound import descriptor - descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data_[current_pos]); - - //Save created descriptor structure and references - ret.push_back(elem); - } - - //Return result - return ret; -} - -//RESOURCES -//Default constructor -pe_base::resource_data_entry::resource_data_entry() - :codepage_(0) -{} - -//Constructor from data -pe_base::resource_data_entry::resource_data_entry(const std::string& data, uint32_t codepage) - :codepage_(codepage), data_(data) -{} - -//Returns resource data codepage -uint32_t pe_base::resource_data_entry::get_codepage() const -{ - return codepage_; -} - -//Returns resource data -const std::string& pe_base::resource_data_entry::get_data() const -{ - return data_; -} - -//Sets resource data codepage -void pe_base::resource_data_entry::set_codepage(uint32_t codepage) -{ - codepage_ = codepage; -} - -//Sets resource data -void pe_base::resource_data_entry::set_data(const std::string& data) -{ - data_ = data; -} - -//Default constructor -pe_base::resource_directory_entry::includes::includes() - :data_(0) -{} - -//Default constructor -pe_base::resource_directory_entry::resource_directory_entry() - :id_(0), includes_data_(false), named_(false) -{} - -//Copy constructor -pe_base::resource_directory_entry::resource_directory_entry(const resource_directory_entry& other) - :id_(other.id_), name_(other.name_), includes_data_(other.includes_data_), named_(other.named_) -{ - //If union'ed pointer is not zero - if(other.ptr_.data_) - { - if(other.includes_data()) - ptr_.data_ = new resource_data_entry(*other.ptr_.data_); - else - ptr_.dir_ = new resource_directory(*other.ptr_.dir_); - } -} - -//Copy assignment operator -pe_base::resource_directory_entry& pe_base::resource_directory_entry::operator=(const resource_directory_entry& other) -{ - release(); - - id_ = other.id_; - name_ = other.name_; - includes_data_ = other.includes_data_; - named_ = other.named_; - - //If other union'ed pointer is not zero - if(other.ptr_.data_) - { - if(other.includes_data()) - ptr_.data_ = new resource_data_entry(*other.ptr_.data_); - else - ptr_.dir_ = new resource_directory(*other.ptr_.dir_); - } - - return *this; -} - -//Destroys included data -void pe_base::resource_directory_entry::release() -{ - //If union'ed pointer is not zero - if(ptr_.data_) - { - if(includes_data()) - delete ptr_.data_; - else - delete ptr_.dir_; - - ptr_.data_ = 0; - } -} - -//Destructor -pe_base::resource_directory_entry::~resource_directory_entry() -{ - release(); -} - -//Returns entry ID -uint32_t pe_base::resource_directory_entry::get_id() const -{ - return id_; -} - -//Returns entry name -const std::wstring& pe_base::resource_directory_entry::get_name() const -{ - return name_; -} - -//Returns true, if entry has name -//Returns false, if entry has ID -bool pe_base::resource_directory_entry::is_named() const -{ - return named_; -} - -//Returns true, if entry includes resource_data_entry -//Returns false, if entry includes resource_directory -bool pe_base::resource_directory_entry::includes_data() const -{ - return includes_data_; -} - -//Returns resource_directory if entry includes it, otherwise throws an exception -const pe_base::resource_directory& pe_base::resource_directory_entry::get_resource_directory() const -{ - if(!ptr_.dir_ || includes_data_) - throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); - - return *ptr_.dir_; -} - -//Returns resource_data_entry if entry includes it, otherwise throws an exception -const pe_base::resource_data_entry& pe_base::resource_directory_entry::get_data_entry() const -{ - if(!ptr_.data_ || !includes_data_) - throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); - - return *ptr_.data_; -} - -//Returns resource_directory if entry includes it, otherwise throws an exception -pe_base::resource_directory& pe_base::resource_directory_entry::get_resource_directory() -{ - if(!ptr_.dir_ || includes_data_) - throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); - - return *ptr_.dir_; -} - -//Returns resource_data_entry if entry includes it, otherwise throws an exception -pe_base::resource_data_entry& pe_base::resource_directory_entry::get_data_entry() -{ - if(!ptr_.data_ || !includes_data_) - throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); - - return *ptr_.data_; -} - -//Sets entry name -void pe_base::resource_directory_entry::set_name(const std::wstring& name) -{ - name_ = name; - named_ = true; - id_ = 0; -} - -//Sets entry ID -void pe_base::resource_directory_entry::set_id(uint32_t id) -{ - id_ = id; - named_ = false; - name_.clear(); -} - -//Adds resource_data_entry -void pe_base::resource_directory_entry::add_data_entry(const resource_data_entry& entry) -{ - release(); - ptr_.data_ = new resource_data_entry(entry); - includes_data_ = true; -} - -//Adds resource_directory -void pe_base::resource_directory_entry::add_resource_directory(const resource_directory& dir) -{ - release(); - ptr_.dir_ = new resource_directory(dir); - includes_data_ = false; -} - -//Default constructor -pe_base::resource_directory::resource_directory() - :characteristics_(0), - timestamp_(0), - major_version_(0), minor_version_(0), - number_of_named_entries_(0), number_of_id_entries_(0) -{} - -//Constructor from data -pe_base::resource_directory::resource_directory(const image_resource_directory& dir) - :characteristics_(dir.Characteristics), - timestamp_(dir.TimeDateStamp), - major_version_(dir.MajorVersion), minor_version_(dir.MinorVersion), - number_of_named_entries_(0), number_of_id_entries_(0) //Set to zero here, calculate on add -{} - -//Returns characteristics of directory -uint32_t pe_base::resource_directory::get_characteristics() const -{ - return characteristics_; -} - -//Returns date and time stamp of directory -uint32_t pe_base::resource_directory::get_timestamp() const -{ - return timestamp_; -} - -//Returns major version of directory -uint16_t pe_base::resource_directory::get_major_version() const -{ - return major_version_; -} - -//Returns minor version of directory -uint16_t pe_base::resource_directory::get_minor_version() const -{ - return minor_version_; -} - -//Returns number of named entries -uint32_t pe_base::resource_directory::get_number_of_named_entries() const -{ - return number_of_named_entries_; -} - -//Returns number of ID entries -uint32_t pe_base::resource_directory::get_number_of_id_entries() const -{ - return number_of_id_entries_; -} - -//Returns resource_directory_entry array -const pe_base::resource_directory::entry_list& pe_base::resource_directory::get_entry_list() const -{ - return entries_; -} - -//Returns resource_directory_entry array -pe_base::resource_directory::entry_list& pe_base::resource_directory::get_entry_list() -{ - return entries_; -} - -//Adds resource_directory_entry -void pe_base::resource_directory::add_resource_directory_entry(const resource_directory_entry& entry) -{ - entries_.push_back(entry); - if(entry.is_named()) - ++number_of_named_entries_; - else - ++number_of_id_entries_; -} - -//Clears resource_directory_entry array -void pe_base::resource_directory::clear_resource_directory_entry_list() -{ - entries_.clear(); - number_of_named_entries_ = 0; - number_of_id_entries_ = 0; -} - -//Sets characteristics of directory -void pe_base::resource_directory::set_characteristics(uint32_t characteristics) -{ - characteristics_ = characteristics; -} - -//Sets date and time stamp of directory -void pe_base::resource_directory::set_timestamp(uint32_t timestamp) -{ - timestamp_ = timestamp; -} - -//Sets number of named entries -void pe_base::resource_directory::set_number_of_named_entries(uint32_t number) -{ - number_of_named_entries_ = number; -} - -//Sets number of ID entries -void pe_base::resource_directory::set_number_of_id_entries(uint32_t number) -{ - number_of_id_entries_ = number; -} - -//Sets major version of directory -void pe_base::resource_directory::set_major_version(uint16_t major_version) -{ - major_version_ = major_version; -} - -//Sets minor version of directory -void pe_base::resource_directory::get_minor_version(uint16_t minor_version) -{ - minor_version_ = minor_version; -} - -//Processes resource directory -const pe_base::resource_directory pe_base::process_resource_directory(uint32_t res_rva, uint32_t offset_to_directory, std::set<uint32_t>& processed) const -{ - resource_directory ret; - - //Check for resource loops - if(!processed.insert(offset_to_directory).second) - throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); - - if(!is_sum_safe(res_rva, offset_to_directory)) - throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); - - //Get root IMAGE_RESOURCE_DIRECTORY - image_resource_directory directory = section_data_from_rva<image_resource_directory>(res_rva + offset_to_directory, section_data_virtual, true); - - ret = resource_directory(directory); - - //Check DWORDs for possible overflows - if(!is_sum_safe(directory.NumberOfIdEntries, directory.NumberOfNamedEntries) - || directory.NumberOfIdEntries + directory.NumberOfNamedEntries >= max_dword / sizeof(image_resource_directory_entry) + sizeof(image_resource_directory)) - throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); - - if(!is_sum_safe(offset_to_directory, sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry)) - || !is_sum_safe(res_rva, offset_to_directory + sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry))) - throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); - - for(unsigned long i = 0; i != static_cast<unsigned long>(directory.NumberOfIdEntries) + directory.NumberOfNamedEntries; ++i) - { - //Read directory entries one by one - image_resource_directory_entry dir_entry = section_data_from_rva<image_resource_directory_entry>( - res_rva + sizeof(image_resource_directory) + i * sizeof(image_resource_directory_entry) + offset_to_directory, section_data_virtual, true); - - //Create directory entry structure - resource_directory_entry entry; - - //If directory is named - if(dir_entry.NameIsString) - { - if(!is_sum_safe(res_rva + sizeof(uint16_t) /* safe */, dir_entry.NameOffset)) - throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); - - //get directory name length - uint16_t directory_name_length = section_data_from_rva<uint16_t>(res_rva + dir_entry.NameOffset, section_data_virtual, true); - - //Check name length - if(section_data_length_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true) < directory_name_length) - throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); - -#ifdef PE_BLISS_WINDOWS - //Set entry UNICODE name - entry.set_name(std::wstring( - reinterpret_cast<const wchar_t*>(section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), - directory_name_length)); -#else - //Set entry UNICODE name - entry.set_name(from_ucs2(u16string( - reinterpret_cast<const unicode16_t*>(section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), - directory_name_length))); -#endif - } - else - { - //Else - set directory ID - entry.set_id(dir_entry.Id); - } - - //If directory entry has another resource directory - if(dir_entry.DataIsDirectory) - { - entry.add_resource_directory(process_resource_directory(res_rva, dir_entry.OffsetToDirectory, processed)); - } - else - { - //If directory entry has data - image_resource_data_entry data_entry = section_data_from_rva<image_resource_data_entry>( - res_rva + dir_entry.OffsetToData, section_data_virtual, true); - - //Check byte count that stated by data entry - if(section_data_length_from_rva(data_entry.OffsetToData, data_entry.OffsetToData, section_data_virtual, true) < data_entry.Size) - throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); - - //Add data entry to directory entry - entry.add_data_entry(resource_data_entry( - std::string(section_data_from_rva(data_entry.OffsetToData, section_data_virtual, true), data_entry.Size), - data_entry.CodePage)); - } - - //Save directory entry - ret.add_resource_directory_entry(entry); - } - - //Return resource directory - return ret; -} - -//Helper function to calculate needed space for resource data -void pe_base::calculate_resource_data_space(const resource_directory& root, uint32_t aligned_offset_from_section_start, uint32_t& needed_size_for_structures, uint32_t& needed_size_for_strings) -{ - needed_size_for_structures += sizeof(image_resource_directory); - for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) - { - needed_size_for_structures += sizeof(image_resource_directory_entry); - - if((*it).is_named()) - needed_size_for_strings += static_cast<uint32_t>(((*it).get_name().length() + 1) * 2 /* unicode */ + sizeof(uint16_t) /* for string length */); - - if(!(*it).includes_data()) - calculate_resource_data_space((*it).get_resource_directory(), aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); - } -} - -//Helper function to calculate needed space for resource data -void pe_base::calculate_resource_data_space(const resource_directory& root, uint32_t needed_size_for_structures, uint32_t needed_size_for_strings, uint32_t& needed_size_for_data, uint32_t& current_data_pos) -{ - for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) - { - if((*it).includes_data()) - { - uint32_t data_size = static_cast<uint32_t>((*it).get_data_entry().get_data().length() + sizeof(image_resource_data_entry) + (align_up(current_data_pos, sizeof(uint32_t)) - current_data_pos) /* alignment */); - needed_size_for_data += data_size; - current_data_pos += data_size; - } - else - { - calculate_resource_data_space((*it).get_resource_directory(), needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); - } - } -} - -//Helper function to rebuild resource directory -void pe_base::rebuild_resource_directory(section& resource_section, resource_directory& root, uint32_t& current_structures_pos, uint32_t& current_data_pos, uint32_t& current_strings_pos, uint32_t offset_from_section_start) -{ - //Create resource directory - image_resource_directory dir = {0}; - dir.Characteristics = root.get_characteristics(); - dir.MajorVersion = root.get_major_version(); - dir.MinorVersion = root.get_minor_version(); - dir.TimeDateStamp = root.get_timestamp(); - - { - resource_directory::entry_list& entries = root.get_entry_list(); - std::sort(entries.begin(), entries.end(), entry_sorter()); - } - - //Calculate number of named and ID entries - for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) - { - if((*it).is_named()) - ++dir.NumberOfNamedEntries; - else - ++dir.NumberOfIdEntries; - } - - std::string& raw_data = resource_section.get_raw_data(); - - //Save resource directory - memcpy(&raw_data[current_structures_pos], &dir, sizeof(dir)); - current_structures_pos += sizeof(dir); - - uint32_t this_current_structures_pos = current_structures_pos; - - current_structures_pos += sizeof(image_resource_directory_entry) * (dir.NumberOfNamedEntries + dir.NumberOfIdEntries); - - //Create all resource directory entries - for(resource_directory::entry_list::iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) - { - image_resource_directory_entry entry; - if((*it).is_named()) - { - entry.Name = 0x80000000 | (current_strings_pos - offset_from_section_start); - uint16_t unicode_length = static_cast<uint16_t>((*it).get_name().length()); - memcpy(&raw_data[current_strings_pos], &unicode_length, sizeof(unicode_length)); - current_strings_pos += sizeof(unicode_length); - -#ifdef PE_BLISS_WINDOWS - memcpy(&raw_data[current_strings_pos], (*it).get_name().c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); -#else - { - u16string str(to_ucs2((*it).get_name())); - memcpy(&raw_data[current_strings_pos], str.c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); - } -#endif - - current_strings_pos += static_cast<unsigned long>((*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); - } - else - { - entry.Name = (*it).get_id(); - } - - if((*it).includes_data()) - { - current_data_pos = align_up(current_data_pos, sizeof(uint32_t)); - image_resource_data_entry data_entry = {0}; - data_entry.CodePage = (*it).get_data_entry().get_codepage(); - data_entry.Size = static_cast<uint32_t>((*it).get_data_entry().get_data().length()); - data_entry.OffsetToData = rva_from_section_offset(resource_section, current_data_pos + sizeof(data_entry)); - - entry.OffsetToData = current_data_pos - offset_from_section_start; - - memcpy(&raw_data[current_data_pos], &data_entry, sizeof(data_entry)); - current_data_pos += sizeof(data_entry); - - memcpy(&raw_data[current_data_pos], (*it).get_data_entry().get_data().data(), data_entry.Size); - current_data_pos += data_entry.Size; - - memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); - this_current_structures_pos += sizeof(entry); - } - else - { - entry.OffsetToData = 0x80000000 | (current_structures_pos - offset_from_section_start); - - memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); - this_current_structures_pos += sizeof(entry); - - rebuild_resource_directory(resource_section, (*it).get_resource_directory(), current_structures_pos, current_data_pos, current_strings_pos, offset_from_section_start); - } - } -} - -//Helper function to rebuild resource directory -bool pe_base::entry_sorter::operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const -{ - if(entry1.is_named() && entry2.is_named()) - return entry1.get_name() < entry2.get_name(); - else if(!entry1.is_named() && !entry2.is_named()) - return entry1.get_id() < entry2.get_id(); - else - return entry1.is_named(); -} - -//Resources rebuilder -//resource_directory - root resource directory -//resources_section - section where resource directory will be placed (must be attached to PE image) -//offset_from_section_start - offset from resources_section raw data start -//resource_directory is non-constant, because it will be sorted -//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers -//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped -//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used -const pe_base::image_directory pe_base::rebuild_resources(resource_directory& info, section& resources_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) -{ - //Check that resources_section is attached to this PE image - if(!section_attached(resources_section)) - throw pe_exception("Resource section must be attached to PE file", pe_exception::section_is_not_attached); - - //Check resource directory correctness - if(info.get_entry_list().empty()) - throw pe_exception("Empty resource directory", pe_exception::incorrect_resource_directory); - - uint32_t aligned_offset_from_section_start = align_up(offset_from_section_start, sizeof(uint32_t)); - uint32_t needed_size_for_structures = aligned_offset_from_section_start - offset_from_section_start; //Calculate needed size for resource tables and data - uint32_t needed_size_for_strings = 0; - uint32_t needed_size_for_data = 0; - - calculate_resource_data_space(info, aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); - - { - uint32_t current_data_pos = aligned_offset_from_section_start + needed_size_for_structures + needed_size_for_strings; - calculate_resource_data_space(info, needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); - } - - uint32_t needed_size = needed_size_for_structures + needed_size_for_strings + needed_size_for_data; - - //Check if exports_section is last one. If it's not, check if there's enough place for resource data - if(&resources_section != &*(sections_.end() - 1) && - (resources_section.empty() || align_up(resources_section.get_size_of_raw_data(), get_file_alignment()) - < needed_size + aligned_offset_from_section_start)) - throw pe_exception("Insufficient space for resource directory", pe_exception::insufficient_space); - - std::string& raw_data = resources_section.get_raw_data(); - - //This will be done only is resources_section is the last section of image or for section with unaligned raw length of data - if(raw_data.length() < needed_size + aligned_offset_from_section_start) - raw_data.resize(needed_size + aligned_offset_from_section_start); //Expand section raw data - - uint32_t current_structures_pos = aligned_offset_from_section_start; - uint32_t current_strings_pos = current_structures_pos + needed_size_for_structures; - uint32_t current_data_pos = current_strings_pos + needed_size_for_strings; - rebuild_resource_directory(resources_section, info, current_structures_pos, current_data_pos, current_strings_pos, aligned_offset_from_section_start); - - //Adjust section raw and virtual sizes - recalculate_section_sizes(resources_section, auto_strip_last_section); - - image_directory ret(rva_from_section_offset(resources_section, aligned_offset_from_section_start), needed_size); - - //If auto-rewrite of PE headers is required - if(save_to_pe_header) - { - set_directory_rva(image_directory_entry_resource, ret.get_rva()); - set_directory_size(image_directory_entry_resource, ret.get_size()); - } - - return ret; -} - -//Returns resources from PE file -const pe_base::resource_directory pe_base::get_resources() const -{ - resource_directory ret; - - if(!has_resources()) - return ret; - - //Get resource directory RVA - uint32_t res_rva = get_directory_rva(image_directory_entry_resource); - - //Store already processed directories to avoid resource loops - std::set<uint32_t> processed; - - //Process all directories (recursion) - ret = process_resource_directory(res_rva, 0, processed); - - return ret; -} - -//Finds resource_directory_entry by ID -pe_base::resource_directory::id_entry_finder::id_entry_finder(uint32_t id) - :id_(id) -{} - -bool pe_base::resource_directory::id_entry_finder::operator()(const resource_directory_entry& entry) const -{ - return !entry.is_named() && entry.get_id() == id_; -} - -//Finds resource_directory_entry by name -pe_base::resource_directory::name_entry_finder::name_entry_finder(const std::wstring& name) - :name_(name) -{} - -bool pe_base::resource_directory::name_entry_finder::operator()(const resource_directory_entry& entry) const -{ - return entry.is_named() && entry.get_name() == name_; -} - -//Finds resource_directory_entry by name or ID (universal) -pe_base::resource_directory::entry_finder::entry_finder(const std::wstring& name) - :name_(name), named_(true) -{} - -pe_base::resource_directory::entry_finder::entry_finder(uint32_t id) - :id_(id), named_(false) -{} - -bool pe_base::resource_directory::entry_finder::operator()(const resource_directory_entry& entry) const -{ - if(named_) - return entry.is_named() && entry.get_name() == name_; - else - return !entry.is_named() && entry.get_id() == id_; -} - -//Returns resource_directory_entry by ID. If not found - throws an exception -const pe_base::resource_directory_entry& pe_base::resource_directory::entry_by_id(uint32_t id) const -{ - entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), id_entry_finder(id)); - if(i == entries_.end()) - throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); - - return *i; -} - -//Returns resource_directory_entry by name. If not found - throws an exception -const pe_base::resource_directory_entry& pe_base::resource_directory::entry_by_name(const std::wstring& name) const -{ - entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), name_entry_finder(name)); - if(i == entries_.end()) - throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); - - return *i; -} - - -//EXCEPTION DIRECTORY (exists on PE+ only) -//Default constructor -pe_base::exception_entry::exception_entry() - :begin_address_(0), end_address_(0), unwind_info_address_(0), - unwind_info_version_(0), - flags_(0), - size_of_prolog_(0), - count_of_codes_(0), - frame_register_(0), - frame_offset_(0) -{} - -//Constructor from data -pe_base::exception_entry::exception_entry(const image_runtime_function_entry& entry, const unwind_info& unwind_info) - :begin_address_(entry.BeginAddress), end_address_(entry.EndAddress), unwind_info_address_(entry.UnwindInfoAddress), - unwind_info_version_(unwind_info.Version), - flags_(unwind_info.Flags), - size_of_prolog_(unwind_info.SizeOfProlog), - count_of_codes_(unwind_info.CountOfCodes), - frame_register_(unwind_info.FrameRegister), - frame_offset_(unwind_info.FrameOffset) -{} - -//Returns starting address of function, affected by exception unwinding -uint32_t pe_base::exception_entry::get_begin_address() const -{ - return begin_address_; -} - -//Returns ending address of function, affected by exception unwinding -uint32_t pe_base::exception_entry::get_end_address() const -{ - return end_address_; -} - -//Returns unwind info address -uint32_t pe_base::exception_entry::get_unwind_info_address() const -{ - return unwind_info_address_; -} - -//Returns UNWIND_INFO structure version -uint8_t pe_base::exception_entry::get_unwind_info_version() const -{ - return unwind_info_version_; -} - -//Returns unwind info flags -uint8_t pe_base::exception_entry::get_flags() const -{ - return flags_; -} - -//The function has an exception handler that should be called -//when looking for functions that need to examine exceptions -bool pe_base::exception_entry::has_exception_handler() const -{ - return (flags_ & unw_flag_ehandler) ? true : false; -} - -//The function has a termination handler that should be called -//when unwinding an exception -bool pe_base::exception_entry::has_termination_handler() const -{ - return (flags_ & unw_flag_uhandler) ? true : false; -} - -//The unwind info structure is not the primary one for the procedure -bool pe_base::exception_entry::is_chaininfo() const -{ - return (flags_ & unw_flag_chaininfo) ? true : false; -} - -//Returns size of function prolog -uint8_t pe_base::exception_entry::get_size_of_prolog() const -{ - return size_of_prolog_; -} - -//Returns number of unwind slots -uint8_t pe_base::exception_entry::get_number_of_unwind_slots() const -{ - return count_of_codes_; -} - -//If the function uses frame pointer -bool pe_base::exception_entry::uses_frame_pointer() const -{ - return frame_register_ != 0; -} - -//Number of the nonvolatile register used as the frame pointer, -//using the same encoding for the operation info field of UNWIND_CODE nodes -uint8_t pe_base::exception_entry::get_frame_pointer_register_number() const -{ - return frame_register_; -} - -//The scaled offset from RSP that is applied to the FP reg when it is established. -//The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 -uint8_t pe_base::exception_entry::get_scaled_rsp_offset() const -{ - return frame_offset_; -} - -//Returns exception directory data (exists on PE+ only) -//Unwind opcodes are not listed, because their format and list are subject to change -const pe_base::exception_entry_list pe_base::get_exception_directory_data() const -{ - exception_entry_list ret; - - //If image doesn't have exception directory, return empty list - if(!has_exception_directory()) - return ret; - - //Check the length in bytes of the section containing exception directory - if(section_data_length_from_rva(get_directory_rva(image_directory_entry_exception), get_directory_rva(image_directory_entry_exception), section_data_virtual, true) < sizeof(image_runtime_function_entry)) - throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); - - unsigned long current_pos = get_directory_rva(image_directory_entry_exception); - - //Check if structures are DWORD-aligned - if(current_pos % sizeof(uint32_t)) - throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); - - //First IMAGE_RUNTIME_FUNCTION_ENTRY table - image_runtime_function_entry exception_table = section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); - - //todo: virtual addresses BeginAddress and EndAddress are not checked to be inside image - while(exception_table.BeginAddress) - { - //Check addresses - if(exception_table.BeginAddress > exception_table.EndAddress) - throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); - - //Get unwind information - unwind_info info = section_data_from_rva<unwind_info>(exception_table.UnwindInfoAddress, section_data_virtual, true); - - //Create exception entry and save it - ret.push_back(exception_entry(exception_table, info)); - - //Go to next exception entry - current_pos += sizeof(image_runtime_function_entry); - exception_table = section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); - } - - return ret; -} - - -//DEBUG -//Default constructor -pe_base::debug_info::debug_info() - :characteristics_(0), - time_stamp_(0), - major_version_(0), minor_version_(0), - type_(0), - size_of_data_(0), - address_of_raw_data_(0), - pointer_to_raw_data_(0), - advanced_info_type_(advanced_info_none) -{} - -//Constructor from data -pe_base::debug_info::debug_info(const image_debug_directory& debug) - :characteristics_(debug.Characteristics), - time_stamp_(debug.TimeDateStamp), - major_version_(debug.MajorVersion), minor_version_(debug.MinorVersion), - type_(debug.Type), - size_of_data_(debug.SizeOfData), - address_of_raw_data_(debug.AddressOfRawData), - pointer_to_raw_data_(debug.PointerToRawData), - advanced_info_type_(advanced_info_none) -{} - -//Returns debug characteristics -uint32_t pe_base::debug_info::get_characteristics() const -{ - return characteristics_; -} - -//Returns debug datetimestamp -uint32_t pe_base::debug_info::get_time_stamp() const -{ - return time_stamp_; -} - -//Returns major version -uint32_t pe_base::debug_info::get_major_version() const -{ - return major_version_; -} - -//Returns minor version -uint32_t pe_base::debug_info::get_minor_version() const -{ - return minor_version_; -} - -//Returns type of debug info (unchecked) -uint32_t pe_base::debug_info::get_type_raw() const -{ - return type_; -} - -//Returns type of debug info from debug_info_type enumeration -pe_base::debug_info::debug_info_type pe_base::debug_info::get_type() const -{ - //Determine debug type - switch(type_) - { - case image_debug_type_coff: - return debug_type_coff; - - case image_debug_type_codeview: - return debug_type_codeview; - - case image_debug_type_fpo: - return debug_type_fpo; - - case image_debug_type_misc: - return debug_type_misc; - - case image_debug_type_exception: - return debug_type_exception; - - case image_debug_type_fixup: - return debug_type_fixup; - - case image_debug_type_omap_to_src: - return debug_type_omap_to_src; - - case image_debug_type_omap_from_src: - return debug_type_omap_from_src; - - case image_debug_type_borland: - return debug_type_borland; - - case image_debug_type_clsid: - return debug_type_clsid; - - case image_debug_type_reserved10: - return debug_type_reserved10; - } - - return debug_type_unknown; -} - -//Returns size of debug data (internal, .pdb or other file doesn't count) -uint32_t pe_base::debug_info::get_size_of_data() const -{ - return size_of_data_; -} - -//Returns RVA of debug info when mapped to memory or zero, if info is not mapped -uint32_t pe_base::debug_info::get_rva_of_raw_data() const -{ - return address_of_raw_data_; -} - -//Returns raw file pointer to raw data -uint32_t pe_base::debug_info::get_pointer_to_raw_data() const -{ - return pointer_to_raw_data_; -} - -//Copy constructor -pe_base::debug_info::debug_info(const debug_info& info) - :characteristics_(info.characteristics_), - time_stamp_(info.time_stamp_), - major_version_(info.major_version_), minor_version_(info.minor_version_), - type_(info.type_), - size_of_data_(info.size_of_data_), - address_of_raw_data_(info.address_of_raw_data_), - pointer_to_raw_data_(info.pointer_to_raw_data_), - advanced_info_type_(info.advanced_info_type_) -{ - copy_advanced_info(info); -} - -//Copy assignment operator -pe_base::debug_info& pe_base::debug_info::operator=(const debug_info& info) -{ - copy_advanced_info(info); - - characteristics_ = info.characteristics_; - time_stamp_ = info.time_stamp_; - major_version_ = info.major_version_; - minor_version_ = info.minor_version_; - type_ = info.type_; - size_of_data_ = info.size_of_data_; - address_of_raw_data_ = info.address_of_raw_data_; - pointer_to_raw_data_ = info.pointer_to_raw_data_; - advanced_info_type_ = info.advanced_info_type_; - - return *this; -} - -//Default constructor -pe_base::debug_info::advanced_info::advanced_info() - :adv_pdb_7_0_info(0) //Zero pointer to advanced data -{} - -//Returns true if advanced debug info is present -bool pe_base::debug_info::advanced_info::is_present() const -{ - return adv_pdb_7_0_info != 0; -} - -//Helper for advanced debug information copying -void pe_base::debug_info::copy_advanced_info(const debug_info& info) -{ - free_present_advanced_info(); - - switch(info.advanced_info_type_) - { - case advanced_info_pdb_7_0: - advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(*info.advanced_debug_info_.adv_pdb_7_0_info); - break; - case advanced_info_pdb_2_0: - advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(*info.advanced_debug_info_.adv_pdb_2_0_info); - break; - case advanced_info_misc: - advanced_debug_info_.adv_misc_info = new misc_debug_info(*info.advanced_debug_info_.adv_misc_info); - break; - case advanced_info_coff: - advanced_debug_info_.adv_coff_info = new coff_debug_info(*info.advanced_debug_info_.adv_coff_info); - break; - default: - break; - } - - advanced_info_type_ = info.advanced_info_type_; -} - -//Helper for clearing any present advanced debug information -void pe_base::debug_info::free_present_advanced_info() -{ - switch(advanced_info_type_) - { - case advanced_info_pdb_7_0: - delete advanced_debug_info_.adv_pdb_7_0_info; - break; - case advanced_info_pdb_2_0: - delete advanced_debug_info_.adv_pdb_2_0_info; - break; - case advanced_info_misc: - delete advanced_debug_info_.adv_misc_info; - break; - case advanced_info_coff: - delete advanced_debug_info_.adv_coff_info; - break; - default: - break; - } - - advanced_debug_info_.adv_pdb_7_0_info = 0; - advanced_info_type_ = advanced_info_none; -} - -//Destructor -pe_base::debug_info::~debug_info() -{ - free_present_advanced_info(); -} - -//Sets advanced debug information -void pe_base::debug_info::set_advanced_debug_info(const pdb_7_0_info& info) -{ - free_present_advanced_info(); - advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(info); - advanced_info_type_ = advanced_info_pdb_7_0; -} - -void pe_base::debug_info::set_advanced_debug_info(const pdb_2_0_info& info) -{ - free_present_advanced_info(); - advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(info); - advanced_info_type_ = advanced_info_pdb_2_0; -} - -void pe_base::debug_info::set_advanced_debug_info(const misc_debug_info& info) -{ - free_present_advanced_info(); - advanced_debug_info_.adv_misc_info = new misc_debug_info(info); - advanced_info_type_ = advanced_info_misc; -} - -void pe_base::debug_info::set_advanced_debug_info(const coff_debug_info& info) -{ - free_present_advanced_info(); - advanced_debug_info_.adv_coff_info = new coff_debug_info(info); - advanced_info_type_ = advanced_info_coff; -} - -//Returns advanced debug information type -pe_base::debug_info::advanced_info_type pe_base::debug_info::get_advanced_info_type() const -{ - return advanced_info_type_; -} - -//Returns advanced debug information or throws an exception, -//if requested information type is not contained by structure -template<> -const pe_base::pdb_7_0_info pe_base::debug_info::get_advanced_debug_info<pe_base::pdb_7_0_info>() const -{ - if(advanced_info_type_ != advanced_info_pdb_7_0) - throw pe_exception("Debug info structure does not contain PDB 7.0 data", pe_exception::advanced_debug_information_request_error); - - return *advanced_debug_info_.adv_pdb_7_0_info; -} - -template<> -const pe_base::pdb_2_0_info pe_base::debug_info::get_advanced_debug_info<pe_base::pdb_2_0_info>() const -{ - if(advanced_info_type_ != advanced_info_pdb_2_0) - throw pe_exception("Debug info structure does not contain PDB 2.0 data", pe_exception::advanced_debug_information_request_error); - - return *advanced_debug_info_.adv_pdb_2_0_info; -} - -template<> -const pe_base::misc_debug_info pe_base::debug_info::get_advanced_debug_info<pe_base::misc_debug_info>() const -{ - if(advanced_info_type_ != advanced_info_misc) - throw pe_exception("Debug info structure does not contain MISC data", pe_exception::advanced_debug_information_request_error); - - return *advanced_debug_info_.adv_misc_info; -} - -template<> -const pe_base::coff_debug_info pe_base::debug_info::get_advanced_debug_info<pe_base::coff_debug_info>() const -{ - if(advanced_info_type_ != advanced_info_coff) - throw pe_exception("Debug info structure does not contain COFF data", pe_exception::advanced_debug_information_request_error); - - return *advanced_debug_info_.adv_coff_info; -} - -//Sets advanced debug information type, if no advanced info structure available -void pe_base::debug_info::set_advanced_info_type(advanced_info_type type) -{ - free_present_advanced_info(); - if(advanced_info_type_ >= advanced_info_codeview_4_0) //Don't set info type for those types, which have advanced info structures - advanced_info_type_ = type; -} - -//Default constructor -pe_base::pdb_7_0_info::pdb_7_0_info() - :age_(0) -{ - memset(&guid_, 0, sizeof(guid_)); -} - -//Constructor from data -pe_base::pdb_7_0_info::pdb_7_0_info(const CV_INFO_PDB70* info) - :age_(info->Age), guid_(info->Signature), - pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination -{} - -//Returns debug PDB 7.0 structure GUID -const guid pe_base::pdb_7_0_info::get_guid() const -{ - return guid_; -} - -//Returns age of build -uint32_t pe_base::pdb_7_0_info::get_age() const -{ - return age_; -} - -//Returns PDB file name / path -const std::string& pe_base::pdb_7_0_info::get_pdb_file_name() const -{ - return pdb_file_name_; -} - -//Default constructor -pe_base::pdb_2_0_info::pdb_2_0_info() - :age_(0), signature_(0) -{} - -//Constructor from data -pe_base::pdb_2_0_info::pdb_2_0_info(const CV_INFO_PDB20* info) - :age_(info->Age), signature_(info->Signature), - pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination -{} - -//Returns debug PDB 2.0 structure signature -uint32_t pe_base::pdb_2_0_info::get_signature() const -{ - return signature_; -} - -//Returns age of build -uint32_t pe_base::pdb_2_0_info::get_age() const -{ - return age_; -} - -//Returns PDB file name / path -const std::string& pe_base::pdb_2_0_info::get_pdb_file_name() const -{ - return pdb_file_name_; -} - -//Default constructor -pe_base::misc_debug_info::misc_debug_info() - :data_type_(0), unicode_(false) -{} - -//Constructor from data -pe_base::misc_debug_info::misc_debug_info(const image_debug_misc* info) - :data_type_(info->DataType), unicode_(info->Unicode ? true : false) -{ - //IMAGE_DEBUG_MISC::Data must be checked before! - if(info->Unicode) - { -#ifdef PE_BLISS_WINDOWS - debug_data_unicode_ = std::wstring(reinterpret_cast<const wchar_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2); -#else - debug_data_unicode_ = from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2)); -#endif - - strip_nullbytes(debug_data_unicode_); //Strip nullbytes in the end of string - } - else - { - debug_data_ansi_ = std::string(reinterpret_cast<const char*>(info->Data), info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */); - strip_nullbytes(debug_data_ansi_); //Strip nullbytes in the end of string - } -} - -//Returns debug data type -uint32_t pe_base::misc_debug_info::get_data_type() const -{ - return data_type_; -} - -//Returns true if data type is exe name -bool pe_base::misc_debug_info::is_exe_name() const -{ - return data_type_ == image_debug_misc_exename; -} - -//Returns true if debug data is UNICODE -bool pe_base::misc_debug_info::is_unicode() const -{ - return unicode_; -} - -//Returns debug data (ANSI) -const std::string& pe_base::misc_debug_info::get_data_ansi() const -{ - return debug_data_ansi_; -} - -//Returns debug data (UNICODE) -const std::wstring& pe_base::misc_debug_info::get_data_unicode() const -{ - return debug_data_unicode_; -} - -//Default constructor -pe_base::coff_debug_info::coff_debug_info() - :number_of_symbols_(0), - lva_to_first_symbol_(0), - number_of_line_numbers_(0), - lva_to_first_line_number_(0), - rva_to_first_byte_of_code_(0), - rva_to_last_byte_of_code_(0), - rva_to_first_byte_of_data_(0), - rva_to_last_byte_of_data_(0) -{} - -//Constructor from data -pe_base::coff_debug_info::coff_debug_info(const image_coff_symbols_header* info) - :number_of_symbols_(info->NumberOfSymbols), - lva_to_first_symbol_(info->LvaToFirstSymbol), - number_of_line_numbers_(info->NumberOfLinenumbers), - lva_to_first_line_number_(info->LvaToFirstLinenumber), - rva_to_first_byte_of_code_(info->RvaToFirstByteOfCode), - rva_to_last_byte_of_code_(info->RvaToLastByteOfCode), - rva_to_first_byte_of_data_(info->RvaToFirstByteOfData), - rva_to_last_byte_of_data_(info->RvaToLastByteOfData) -{} - -//Returns number of symbols -uint32_t pe_base::coff_debug_info::get_number_of_symbols() const -{ - return number_of_symbols_; -} - -//Returns virtual address of the first symbol -uint32_t pe_base::coff_debug_info::get_lva_to_first_symbol() const -{ - return lva_to_first_symbol_; -} - -//Returns number of line-number entries -uint32_t pe_base::coff_debug_info::get_number_of_line_numbers() const -{ - return number_of_line_numbers_; -} - -//Returns virtual address of the first line-number entry -uint32_t pe_base::coff_debug_info::get_lva_to_first_line_number() const -{ - return lva_to_first_line_number_; -} - -//Returns relative virtual address of the first byte of code -uint32_t pe_base::coff_debug_info::get_rva_to_first_byte_of_code() const -{ - return rva_to_first_byte_of_code_; -} - -//Returns relative virtual address of the last byte of code -uint32_t pe_base::coff_debug_info::get_rva_to_last_byte_of_code() const -{ - return rva_to_last_byte_of_code_; -} - -//Returns relative virtual address of the first byte of data -uint32_t pe_base::coff_debug_info::get_rva_to_first_byte_of_data() const -{ - return rva_to_first_byte_of_data_; -} - -//Returns relative virtual address of the last byte of data -uint32_t pe_base::coff_debug_info::get_rva_to_last_byte_of_data() const -{ - return rva_to_last_byte_of_data_; -} - -//Returns COFF symbols list -const pe_base::coff_debug_info::coff_symbols_list& pe_base::coff_debug_info::get_symbols() const -{ - return symbols_; -} - -//Adds COFF symbol -void pe_base::coff_debug_info::add_symbol(const coff_symbol& sym) -{ - symbols_.push_back(sym); -} - -//Default constructor -pe_base::coff_debug_info::coff_symbol::coff_symbol() - :storage_class_(0), - index_(0), - section_number_(0), rva_(0), - type_(0), - is_filename_(false) -{} - -//Returns storage class -uint32_t pe_base::coff_debug_info::coff_symbol::get_storage_class() const -{ - return storage_class_; -} - -//Returns symbol index -uint32_t pe_base::coff_debug_info::coff_symbol::get_index() const -{ - return index_; -} - -//Returns section number -uint32_t pe_base::coff_debug_info::coff_symbol::get_section_number() const -{ - return section_number_; -} - -//Returns RVA -uint32_t pe_base::coff_debug_info::coff_symbol::get_rva() const -{ - return rva_; -} - -//Returns true if structure contains file name -bool pe_base::coff_debug_info::coff_symbol::is_file() const -{ - return is_filename_; -} - -//Returns text data (symbol or file name) -const std::string& pe_base::coff_debug_info::coff_symbol::get_symbol() const -{ - return name_; -} - -//Sets storage class -void pe_base::coff_debug_info::coff_symbol::set_storage_class(uint32_t storage_class) -{ - storage_class_ = storage_class; -} - -//Sets symbol index -void pe_base::coff_debug_info::coff_symbol::set_index(uint32_t index) -{ - index_ = index; -} - -//Sets section number -void pe_base::coff_debug_info::coff_symbol::set_section_number(uint32_t section_number) -{ - section_number_ = section_number; -} - -//Sets RVA -void pe_base::coff_debug_info::coff_symbol::set_rva(uint32_t rva) -{ - rva_ = rva; -} - -//Sets file name -void pe_base::coff_debug_info::coff_symbol::set_file_name(const std::string& file_name) -{ - name_ = file_name; - is_filename_ = true; -} - -//Sets symbol name -void pe_base::coff_debug_info::coff_symbol::set_symbol_name(const std::string& symbol_name) -{ - name_ = symbol_name; - is_filename_ = false; -} - -//Returns type -uint16_t pe_base::coff_debug_info::coff_symbol::get_type() const -{ - return type_; -} - -//Sets type -void pe_base::coff_debug_info::coff_symbol::set_type(uint16_t type) -{ - type_ = type; -} - -//Returns debug information list -const pe_base::debug_info_list pe_base::get_debug_information() const -{ - debug_info_list ret; - - //If there's no debug directory, return empty list - if(!has_debug()) - return ret; - - //Check the length in bytes of the section containing debug directory - if(section_data_length_from_rva(get_directory_rva(image_directory_entry_debug), get_directory_rva(image_directory_entry_debug), section_data_virtual, true) < sizeof(image_debug_directory)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - unsigned long current_pos = get_directory_rva(image_directory_entry_debug); - - //First IMAGE_DEBUG_DIRECTORY table - image_debug_directory directory = section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); - - if(!is_sum_safe(get_directory_rva(image_directory_entry_debug), get_directory_size(image_directory_entry_debug))) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Iterate over all IMAGE_DEBUG_DIRECTORY directories - while(directory.PointerToRawData - && current_pos < get_directory_rva(image_directory_entry_debug) + get_directory_size(image_directory_entry_debug)) - { - //Create debug information structure - debug_info info(directory); - - //Find raw debug data - debug_data_list::const_iterator it = debug_data_.find(directory.PointerToRawData); - if(it != debug_data_.end()) //If it exists, we'll do some detailed debug info research - { - const std::string& debug_data = (*it).second; - switch(directory.Type) - { - case image_debug_type_coff: - { - //Check data length - if(debug_data.length() < sizeof(image_coff_symbols_header)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Get coff header structure pointer - const image_coff_symbols_header* coff = reinterpret_cast<const image_coff_symbols_header*>(debug_data.data()); - - //Check possible overflows - if(coff->NumberOfSymbols >= max_dword / sizeof(image_symbol) - || !is_sum_safe(coff->NumberOfSymbols * sizeof(image_symbol), coff->LvaToFirstSymbol)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Check data length again - if(debug_data.length() < coff->NumberOfSymbols * sizeof(image_symbol) + coff->LvaToFirstSymbol) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Create COFF debug info structure - coff_debug_info coff_info(coff); - - //Enumerate debug symbols data - for(uint32_t i = 0; i < coff->NumberOfSymbols; ++i) - { - //Safe sum (checked above) - const image_symbol* sym = reinterpret_cast<const image_symbol*>(debug_data.data() + i * sizeof(image_symbol) + coff->LvaToFirstSymbol); - - coff_debug_info::coff_symbol symbol; - symbol.set_index(i); //Save symbol index - symbol.set_storage_class(sym->StorageClass); //Save storage class - symbol.set_type(sym->Type); //Save storage class - - //Check data length again - if(!is_sum_safe(i, sym->NumberOfAuxSymbols) - || (i + sym->NumberOfAuxSymbols) > coff->NumberOfSymbols - || debug_data.length() < (i + 1) * sizeof(image_symbol) + coff->LvaToFirstSymbol + sym->NumberOfAuxSymbols * sizeof(image_symbol)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //If symbol is filename - if(sym->StorageClass == image_sym_class_file) - { - //Save file name, it is situated just after this IMAGE_SYMBOL structure - std::string file_name(reinterpret_cast<const char*>(debug_data.data() + (i + 1) * sizeof(image_symbol)), sym->NumberOfAuxSymbols * sizeof(image_symbol)); - strip_nullbytes(file_name); - symbol.set_file_name(file_name); - - //Save symbol info - coff_info.add_symbol(symbol); - - //Move to next symbol - i += sym->NumberOfAuxSymbols; - continue; - } - - //Dump some other symbols - if(((sym->StorageClass == image_sym_class_static) - && (sym->NumberOfAuxSymbols == 0) - && (sym->SectionNumber == 1)) - || - ((sym->StorageClass == image_sym_class_external) - && ISFCN(sym->Type) - && (sym->SectionNumber > 0)) - ) - { - //Save RVA and section number - symbol.set_section_number(sym->SectionNumber); - symbol.set_rva(sym->Value); - - //If symbol has short name - if(sym->N.Name.Short) - { - //Copy and save symbol name - char name_buff[9]; - memcpy(name_buff, sym->N.ShortName, 8); - name_buff[8] = '\0'; - symbol.set_symbol_name(name_buff); - } - else - { - //Symbol has long name - - //Check possible overflows - if(!is_sum_safe(coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol), sym->N.Name.Long)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Here we have an offset to the string table - uint32_t symbol_offset = coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol) + sym->N.Name.Long; - - //Check data length - if(debug_data.length() < symbol_offset) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Check symbol name for null-termination - if(!is_null_terminated(debug_data.data() + symbol_offset, debug_data.length() - symbol_offset)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Save symbol name - symbol.set_symbol_name(debug_data.data() + symbol_offset); - } - - //Save symbol info - coff_info.add_symbol(symbol); - - //Move to next symbol - i += sym->NumberOfAuxSymbols; - continue; - } - } - - info.set_advanced_debug_info(coff_info); - } - break; - - case image_debug_type_codeview: - { - //Check data length - if(debug_data.length() < sizeof(OMFSignature*)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Get POMFSignature structure pointer from the very beginning of debug data - const OMFSignature* sig = reinterpret_cast<const OMFSignature*>(debug_data.data()); - if(!memcmp(sig->Signature, "RSDS", 4)) - { - //Signature is "RSDS" - PDB 7.0 - - //Check data length - if(debug_data.length() < sizeof(CV_INFO_PDB70)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - const CV_INFO_PDB70* pdb_data = reinterpret_cast<const CV_INFO_PDB70*>(debug_data.data()); - - //Check PDB file name null-termination - if(!is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB70) - 1 /* BYTE of filename in structure */))) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - info.set_advanced_debug_info(pdb_7_0_info(pdb_data)); - } - else if(!memcmp(sig->Signature, "NB10", 4)) - { - //Signature is "NB10" - PDB 2.0 - - //Check data length - if(debug_data.length() < sizeof(CV_INFO_PDB20)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - const CV_INFO_PDB20* pdb_data = reinterpret_cast<const CV_INFO_PDB20*>(debug_data.data()); - - //Check PDB file name null-termination - if(!is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB20) - 1 /* BYTE of filename in structure */))) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - info.set_advanced_debug_info(pdb_2_0_info(pdb_data)); - } - else if(!memcmp(sig->Signature, "NB09", 4)) - { - //CodeView 4.0, no structures available - info.set_advanced_info_type(debug_info::advanced_info_codeview_4_0); - } - else if(!memcmp(sig->Signature, "NB11", 4)) - { - //CodeView 5.0, no structures available - info.set_advanced_info_type(debug_info::advanced_info_codeview_5_0); - } - else if(!memcmp(sig->Signature, "NB05", 4)) - { - //Other CodeView, no structures available - info.set_advanced_info_type(debug_info::advanced_info_codeview); - } - } - - break; - - case image_debug_type_misc: - { - //Check data length - if(debug_data.length() < sizeof(image_debug_misc)) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Get misc structure pointer - const image_debug_misc* misc_data = reinterpret_cast<const image_debug_misc*>(debug_data.data()); - - //Check misc data length - if(debug_data.length() < misc_data->Length /* Total length of record */) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Save advanced information - info.set_advanced_debug_info(misc_debug_info(misc_data)); - } - break; - } - } - - //Save debug information structure - ret.push_back(info); - - //Check possible overflow - if(!is_sum_safe(current_pos, sizeof(image_debug_directory))) - throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); - - //Go to next debug entry - current_pos += sizeof(image_debug_directory); - directory = section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); - } - - return ret; -} - - -//.NET -pe_base::basic_dotnet_info::basic_dotnet_info() -{ - memset(&header_, 0, sizeof(header_)); -} - -//Constructor from data -pe_base::basic_dotnet_info::basic_dotnet_info(const image_cor20_header& header) - :header_(header) -{} - -//Returns major runtime version -uint16_t pe_base::basic_dotnet_info::get_major_runtime_version() const -{ - return header_.MajorRuntimeVersion; -} - -//Returns minor runtime version -uint16_t pe_base::basic_dotnet_info::get_minor_runtime_version() const -{ - return header_.MinorRuntimeVersion; -} - -//Returns RVA of metadata (symbol table and startup information) -uint32_t pe_base::basic_dotnet_info::get_rva_of_metadata() const -{ - return header_.MetaData.VirtualAddress; -} - -//Returns size of metadata (symbol table and startup information) -uint32_t pe_base::basic_dotnet_info::get_size_of_metadata() const -{ - return header_.MetaData.Size; -} - -//Returns flags -uint32_t pe_base::basic_dotnet_info::get_flags() const -{ - return header_.Flags; -} - -//Returns true if entry point is native -bool pe_base::basic_dotnet_info::is_native_entry_point() const +void pe_base::set_image_base_64(uint64_t base) { - return (header_.Flags & comimage_flags_native_entrypoint) ? true : false; + props_->set_image_base_64(base); } -//Returns true if 32 bit required -bool pe_base::basic_dotnet_info::is_32bit_required() const +//Sets heap size commit for PE32 and PE64 respectively +void pe_base::set_heap_size_commit(uint32_t size) { - return (header_.Flags & comimage_flags_32bitrequired) ? true : false; + props_->set_heap_size_commit(size); } -//Returns true if image is IL library -bool pe_base::basic_dotnet_info::is_il_library() const +void pe_base::set_heap_size_commit(uint64_t size) { - return (header_.Flags & comimage_flags_il_library) ? true : false; + props_->set_heap_size_commit(size); } -//Returns true if image uses IL only -bool pe_base::basic_dotnet_info::is_il_only() const +//Sets heap size reserve for PE32 and PE64 respectively +void pe_base::set_heap_size_reserve(uint32_t size) { - return (header_.Flags & comimage_flags_ilonly) ? true : false; + props_->set_heap_size_reserve(size); } -//Returns entry point RVA (if entry point is native) -//Returns entry point managed token (if entry point is managed) -uint32_t pe_base::basic_dotnet_info::get_entry_point_rva_or_token() const +void pe_base::set_heap_size_reserve(uint64_t size) { - return header_.EntryPointToken; + props_->set_heap_size_reserve(size); } -//Returns RVA of managed resources -uint32_t pe_base::basic_dotnet_info::get_rva_of_resources() const +//Sets stack size commit for PE32 and PE64 respectively +void pe_base::set_stack_size_commit(uint32_t size) { - return header_.Resources.VirtualAddress; + props_->set_stack_size_commit(size); } -//Returns size of managed resources -uint32_t pe_base::basic_dotnet_info::get_size_of_resources() const +void pe_base::set_stack_size_commit(uint64_t size) { - return header_.Resources.Size; + props_->set_stack_size_commit(size); } -//Returns RVA of strong name signature -uint32_t pe_base::basic_dotnet_info::get_rva_of_strong_name_signature() const +//Sets stack size reserve for PE32 and PE64 respectively +void pe_base::set_stack_size_reserve(uint32_t size) { - return header_.StrongNameSignature.VirtualAddress; + props_->set_stack_size_reserve(size); } -//Returns size of strong name signature -uint32_t pe_base::basic_dotnet_info::get_size_of_strong_name_signature() const +void pe_base::set_stack_size_reserve(uint64_t size) { - return header_.StrongNameSignature.Size; + props_->set_stack_size_reserve(size); } -//Returns RVA of code manager table -uint32_t pe_base::basic_dotnet_info::get_rva_of_code_manager_table() const +//Returns heap size commit for PE32 and PE64 respectively +uint32_t pe_base::get_heap_size_commit_32() const { - return header_.CodeManagerTable.VirtualAddress; + return props_->get_heap_size_commit_32(); } -//Returns size of code manager table -uint32_t pe_base::basic_dotnet_info::get_size_of_code_manager_table() const +uint64_t pe_base::get_heap_size_commit_64() const { - return header_.CodeManagerTable.Size; + return props_->get_heap_size_commit_64(); } -//Returns RVA of VTable fixups -uint32_t pe_base::basic_dotnet_info::get_rva_of_vtable_fixups() const +//Returns heap size reserve for PE32 and PE64 respectively +uint32_t pe_base::get_heap_size_reserve_32() const { - return header_.VTableFixups.VirtualAddress; + return props_->get_heap_size_reserve_32(); } -//Returns size of VTable fixups -uint32_t pe_base::basic_dotnet_info::get_size_of_vtable_fixups() const +uint64_t pe_base::get_heap_size_reserve_64() const { - return header_.VTableFixups.Size; + return props_->get_heap_size_reserve_64(); } -//Returns RVA of export address table jumps -uint32_t pe_base::basic_dotnet_info::get_rva_of_export_address_table_jumps() const +//Returns stack size commit for PE32 and PE64 respectively +uint32_t pe_base::get_stack_size_commit_32() const { - return header_.ExportAddressTableJumps.VirtualAddress; + return props_->get_stack_size_commit_32(); } -//Returns size of export address table jumps -uint32_t pe_base::basic_dotnet_info::get_size_of_export_address_table_jumps() const +uint64_t pe_base::get_stack_size_commit_64() const { - return header_.ExportAddressTableJumps.Size; + return props_->get_stack_size_commit_64(); } -//Returns RVA of managed native header -//(precompiled header info, usually set to zero, for internal use) -uint32_t pe_base::basic_dotnet_info::get_rva_of_managed_native_header() const +//Returns stack size reserve for PE32 and PE64 respectively +uint32_t pe_base::get_stack_size_reserve_32() const { - return header_.ManagedNativeHeader.VirtualAddress; + return props_->get_stack_size_reserve_32(); } -//Returns size of managed native header -//(precompiled header info, usually set to zero, for internal use) -uint32_t pe_base::basic_dotnet_info::get_size_of_managed_native_header() const +uint64_t pe_base::get_stack_size_reserve_64() const { - return header_.ManagedNativeHeader.Size; + return props_->get_stack_size_reserve_64(); } -//Returns basic .NET information -//If image is not native, throws an exception -const pe_base::basic_dotnet_info pe_base::get_basic_dotnet_info() const -{ - //If there's no debug directory, return empty list - if(!is_dotnet()) - throw pe_exception("Image does not have managed code", pe_exception::image_does_not_have_managed_code); - - //Return basic .NET information - return basic_dotnet_info(section_data_from_rva<image_cor20_header>(get_directory_rva(image_directory_entry_com_descriptor), section_data_virtual, true)); -} - - -//ENTROPY -//Calculates entropy for PE image section -double pe_base::calculate_entropy(const section& s) -{ - if(s.get_raw_data().empty()) //Don't count entropy for empty sections - throw pe_exception("Section is empty", pe_exception::section_is_empty); - - return calculate_entropy(s.get_raw_data().data(), s.get_raw_data().length()); -} - -//Calculates entropy from bytes count -double pe_base::calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length) -{ - double entropy = 0.; //Entropy result value - //Calculate entropy - for(uint32_t i = 0; i < 256; ++i) - { - double temp = static_cast<double>(byte_count[i]) / total_length; - if(temp > 0.) - entropy += std::abs(temp * (std::log(temp) * log_2)); - } - - return entropy; -} - -//Calculates entropy for istream (from current position of stream) -double pe_base::calculate_entropy(std::istream& file) +//Returns heap size commit for PE32 +void pe_base::get_heap_size_commit(uint32_t& size) const { - uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes - - if(file.bad()) - throw pe_exception("Stream is bad", pe_exception::stream_is_bad); - - std::streamoff pos = file.tellg(); - - std::streamoff length = get_file_size(file); - length -= file.tellg(); - - if(!length) //Don't calculate entropy for empty buffers - throw pe_exception("Data length is zero", pe_exception::data_is_empty); - - //Count bytes - for(std::streamoff i = 0; i != length; ++i) - ++byte_count[static_cast<unsigned char>(file.get())]; - - file.seekg(pos); - - return calculate_entropy(byte_count, length); + size = get_heap_size_commit_32(); } -//Calculates entropy for data block -double pe_base::calculate_entropy(const char* data, size_t length) +//Returns heap size commit for PE32/PE64 +void pe_base::get_heap_size_commit(uint64_t& size) const { - uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes - - if(!length) //Don't calculate entropy for empty buffers - throw pe_exception("Data length is zero", pe_exception::data_is_empty); - - //Count bytes - for(size_t i = 0; i != length; ++i) - ++byte_count[static_cast<unsigned char>(data[i])]; - - return calculate_entropy(byte_count, length); + size = get_heap_size_commit_64(); } -//Calculates entropy for this PE file (only section data) -double pe_base::calculate_entropy() const +//Returns heap size reserve for PE32 +void pe_base::get_heap_size_reserve(uint32_t& size) const { - uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes - - size_t total_data_length = 0; - - //Count bytes for each section - for(section_list::const_iterator it = sections_.begin(); it != sections_.end(); ++it) - { - const std::string& data = (*it).get_raw_data(); - size_t length = data.length(); - total_data_length += length; - for(size_t i = 0; i != length; ++i) - ++byte_count[static_cast<unsigned char>(data[i])]; - } - - return calculate_entropy(byte_count, total_data_length); + size = get_heap_size_reserve_32(); } -pe_base::section_ptr_finder::section_ptr_finder(const section& s) - :s_(s) -{} - -bool pe_base::section_ptr_finder::operator()(const section& s) const +//Returns heap size reserve for PE32/PE64 +void pe_base::get_heap_size_reserve(uint64_t& size) const { - return &s == &s_; + size = get_heap_size_reserve_64(); } -//Default constructor -pe_base::image_directory::image_directory() - :rva_(0), size_(0) -{} - -//Constructor from data -pe_base::image_directory::image_directory(uint32_t rva, uint32_t size) - :rva_(rva), size_(size) -{} - -//Returns RVA -uint32_t pe_base::image_directory::get_rva() const +//Returns stack size commit for PE32 +void pe_base::get_stack_size_commit(uint32_t& size) const { - return rva_; + size = get_stack_size_commit_32(); } -//Returns size -uint32_t pe_base::image_directory::get_size() const +//Returns stack size commit for PE32/PE64 +void pe_base::get_stack_size_commit(uint64_t& size) const { - return size_; + size = get_stack_size_commit_64(); } -//Sets RVA -void pe_base::image_directory::set_rva(uint32_t rva) +//Returns stack size reserve for PE32 +void pe_base::get_stack_size_reserve(uint32_t& size) const { - rva_ = rva; + size = get_stack_size_reserve_32(); } -//Sets size -void pe_base::image_directory::set_size(uint32_t size) +//Returns stack size reserve for PE32/PE64 +void pe_base::get_stack_size_reserve(uint64_t& size) const { - size_ = size; + size = get_stack_size_reserve_64(); } //Realigns file (changes file alignment) @@ -5345,8 +1569,8 @@ void pe_base::recalculate_section_sizes(section& s, bool auto_strip) } //Can occur only for last section - if(align_up(s.get_virtual_size(), get_section_alignment()) < align_up(s.get_size_of_raw_data(), get_file_alignment())) - set_section_virtual_size(s, align_up(s.get_size_of_raw_data(), get_section_alignment())); //Recalculate section virtual size + if(pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) < pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment())) + set_section_virtual_size(s, pe_utils::align_up(s.get_size_of_raw_data(), get_section_alignment())); //Recalculate section virtual size } //Returns data from the beginning of image @@ -5356,57 +1580,80 @@ const std::string& pe_base::get_full_headers_data() const return full_headers_data_; } -#ifndef PE_BLISS_WINDOWS -const u16string pe_base::to_ucs2(const std::wstring& str) +const pe_base::debug_data_list& pe_base::get_raw_debug_data_list() const { - u16string ret; - if(str.empty()) - return ret; + return debug_data_; +} - ret.resize(str.length()); +//Sets number of sections +void pe_base::set_number_of_sections(uint16_t number) +{ + props_->set_number_of_sections(number); +} - iconv_t conv = iconv_open("UCS-2", "WCHAR_T"); - if(conv == reinterpret_cast<iconv_t>(-1)) - throw pe_exception("Error opening iconv", pe_exception::encoding_convertion_error); +//Sets size of image +void pe_base::set_size_of_image(uint32_t size) +{ + props_->set_size_of_image(size); +} - size_t inbytesleft = str.length() * sizeof(wchar_t); - size_t outbytesleft = ret.length() * sizeof(unicode16_t); - const wchar_t* in_pos = str.c_str(); - unicode16_t* out_pos = &ret[0]; +//Sets size of headers +void pe_base::set_size_of_headers(uint32_t size) +{ + props_->set_size_of_headers(size); +} - size_t result = iconv(conv, const_cast<char**>(reinterpret_cast<const char**>(&in_pos)), &inbytesleft, reinterpret_cast<char**>(&out_pos), &outbytesleft); - iconv_close(conv); - - if(result == static_cast<size_t>(-1)) - throw pe_exception("Iconv error", pe_exception::encoding_convertion_error); +//Sets size of optional headers +void pe_base::set_size_of_optional_header(uint16_t size) +{ + props_->set_size_of_optional_header(size); +} - return ret; +//Returns nt headers data pointer +char* pe_base::get_nt_headers_ptr() +{ + return props_->get_nt_headers_ptr(); } -const std::wstring pe_base::from_ucs2(const u16string& str) +//Returns nt headers data pointer +const char* pe_base::get_nt_headers_ptr() const { - std::wstring ret; - if(str.empty()) - return ret; + return props_->get_nt_headers_ptr(); +} - ret.resize(str.length()); +//Returns sizeof() nt headers +uint32_t pe_base::get_sizeof_nt_header() const +{ + return props_->get_sizeof_nt_header(); +} - iconv_t conv = iconv_open("WCHAR_T", "UCS-2"); - if(conv == reinterpret_cast<iconv_t>(-1)) - throw pe_exception("Error opening iconv", pe_exception::encoding_convertion_error); +//Returns sizeof() optional headers +uint32_t pe_base::get_sizeof_opt_headers() const +{ + return props_->get_sizeof_opt_headers(); +} - size_t inbytesleft = str.length() * sizeof(unicode16_t); - size_t outbytesleft = ret.length() * sizeof(wchar_t); - const unicode16_t* in_pos = str.c_str(); - wchar_t* out_pos = &ret[0]; +//Sets file alignment (no checks) +void pe_base::set_file_alignment_unchecked(uint32_t alignment) +{ + props_->set_file_alignment_unchecked(alignment); +} - size_t result = iconv(conv, const_cast<char**>(reinterpret_cast<const char**>(&in_pos)), &inbytesleft, reinterpret_cast<char**>(&out_pos), &outbytesleft); - iconv_close(conv); +//Sets base of code +void pe_base::set_base_of_code(uint32_t base) +{ + props_->set_base_of_code(base); +} - if(result == static_cast<size_t>(-1)) - throw pe_exception("Iconv error", pe_exception::encoding_convertion_error); +//Returns base of code +uint32_t pe_base::get_base_of_code() const +{ + return props_->get_base_of_code(); +} - return ret; +//Returns needed magic of image +uint32_t pe_base::get_needed_magic() const +{ + return props_->get_needed_magic(); } -#endif } diff --git a/pe_lib/pe_base.h b/pe_lib/pe_base.h index c5b5e67..f25e269 100644 --- a/pe_lib/pe_base.h +++ b/pe_lib/pe_base.h @@ -3,21 +3,21 @@ #include <vector> #include <istream> #include <ostream> -#include <algorithm> -#include <utility> #include <map> -#include <set> #include "pe_exception.h" #include "pe_structures.h" +#include "utils.h" +#include "pe_section.h" +#include "pe_properties.h" //Please don't remove this information from header -//PEBliss 0.2.4 +//PEBliss 1.0.0 //(c) DX 2011 - 2012, http://kaimi.ru -//Free to use for commertial and non-commertial purposes, modify and distribute +//Free to use for commertial and non-commertial purposes, modification and distribution // == more important == -//TODO: create sample-based tests -//TODO: check for correctness of exports sorting (ABC...abc) +//TODO: compact import rebuilder +//TODO: remove sections in the middle //== less important == //TODO: relocations that take more than one element (seems to be not possible in Windows PE, but anyway) //TODO: delay import directory @@ -28,195 +28,53 @@ namespace pe_bliss { -//Portable executable base class +//Portable executable class class pe_base { -public: //STUB OVERLAY - //Rich data overlay structure of Microsoft Visual Studio - struct rich_data - { - public: - //Default constructor - rich_data(); - - public: //Getters - //Who knows, what these fields mean... - uint32_t get_number() const; - uint32_t get_version() const; - uint32_t get_times() const; - - public: //Setters, used by PE library only - void set_number(uint32_t number); - void set_version(uint32_t version); - void set_times(uint32_t times); - - private: - uint32_t number_; - uint32_t version_; - uint32_t times_; - }; +public: //CONSTRUCTORS + //Constructor from stream + pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data = true); + //Constructor of empty PE-file + explicit pe_base(const pe_properties& props, uint32_t section_alignment = 0x1000, bool dll = false, uint16_t subsystem = pe_win::image_subsystem_windows_gui); -public: //GENERAL - //Destructor - virtual ~pe_base(); - - -public: //SECTIONS - //Struct representing image section - struct section - { - public: //Friends - friend class pe_base; - - public: - //Default constructor - section(); - - //Sets the name of section (stripped to 8 characters) - void set_name(const std::string& name); - - //Returns the name of section - const std::string get_name() const; - - //Changes attributes of section - section& readable(bool readable); - section& writeable(bool writeable); - section& executable(bool executable); - section& shared(bool shared); - section& discardable(bool discardable); - - //Returns attributes of section - bool readable() const; - bool writeable() const; - bool executable() const; - bool shared() const; - bool discardable() const; - - //Returns true if section has no RAW data - bool empty() const; - - //Returns raw section data from file image - std::string& get_raw_data(); - //Returns raw section data from file image - const std::string& get_raw_data() const; - //Returns mapped virtual section data - const std::string& get_virtual_data() const; - //Returns mapped virtual section data - std::string& get_virtual_data(); - - //Header operations - - //Returns section virtual size - uint32_t get_virtual_size() const; - //Returns section virtual address (RVA) - uint32_t get_virtual_address() const; - //Returns size of section raw data - uint32_t get_size_of_raw_data() const; - //Returns pointer to raw section data in PE file - uint32_t get_pointer_to_raw_data() const; - //Returns section characteristics - uint32_t get_characteristics() const; - - public: //Setters - //Sets size of raw section data - void set_size_of_raw_data(uint32_t size_of_raw_data); - //Sets pointer to section raw data - void set_pointer_to_raw_data(uint32_t pointer_to_raw_data); - //Sets section characteristics - void set_characteristics(uint32_t characteristics); - //Sets raw section data from file image - void set_raw_data(const std::string& data); - - public: //Setters, be careful - //Sets section virtual size (doesn't set internal aligned virtual size, changes only header value) - //Better use pe_base::set_section_virtual_size - void set_virtual_size(uint32_t virtual_size); - //Sets section virtual address - void set_virtual_address(uint32_t virtual_address); - - private: - //Section header - pe_win::image_section_header header_; - - //Aligned sizes of section - uint32_t raw_size_aligned_; - uint32_t virtual_size_aligned_; - - //Maps virtual section data - void map_virtual() const; - - //Unmaps virtual section data - void unmap_virtual() const; - - //Set flag (attribute) of section - section& set_flag(uint32_t flag, bool setflag); - - //Old size of section (stored after mapping of virtual section memory) - mutable std::size_t old_size_; - - //Section raw/virtual data - mutable std::string raw_data_; - }; + pe_base(const pe_base& pe); + pe_base& operator=(const pe_base& pe); +public: + ~pe_base(); public: //STUB //Strips stub MSVS overlay, if any void strip_stub_overlay(); //Fills stub MSVS overlay with specified byte void fill_stub_overlay(char c); + //Sets stub MSVS overlay + void set_stub_overlay(const std::string& data); //Returns stub overlay contents const std::string& get_stub_overlay() const; - - //Returns a vector with rich data (stub overlay) - typedef std::vector<rich_data> rich_data_list; - const rich_data_list get_rich_data() const; public: //DIRECTORIES - //Structure representing image directory data - struct image_directory - { - public: - //Default constructor - image_directory(); - //Constructor from data - image_directory(uint32_t rva, uint32_t size); - - //Returns RVA - uint32_t get_rva() const; - //Returns size - uint32_t get_size() const; - - //Sets RVA - void set_rva(uint32_t rva); - //Sets size - void set_size(uint32_t size); - - private: - uint32_t rva_; - uint32_t size_; - }; - //Returns true if directory exists - virtual bool directory_exists(uint32_t id) const = 0; + bool directory_exists(uint32_t id) const; //Removes directory - virtual void remove_directory(uint32_t id) = 0; + void remove_directory(uint32_t id); //Returns directory RVA - virtual uint32_t get_directory_rva(uint32_t id) const = 0; + uint32_t get_directory_rva(uint32_t id) const; //Returns directory size - virtual uint32_t get_directory_size(uint32_t id) const = 0; + uint32_t get_directory_size(uint32_t id) const; //Sets directory RVA (just a value of PE header, no moving occurs) - virtual void set_directory_rva(uint32_t id, uint32_t rva) = 0; + void set_directory_rva(uint32_t id, uint32_t rva); //Sets directory size (just a value of PE header, no moving occurs) - virtual void set_directory_size(uint32_t id, uint32_t size) = 0; + void set_directory_size(uint32_t id, uint32_t size); //Strips only zero DATA_DIRECTORY entries to count = min_count //Returns resulting number of data directories //strip_iat_directory - if true, even not empty IAT directory will be stripped - virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true) = 0; + uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true); //Returns true if image has import directory bool has_imports() const; @@ -244,58 +102,58 @@ public: //DIRECTORIES bool has_debug() const; //Returns subsystem value - virtual uint16_t get_subsystem() const = 0; + uint16_t get_subsystem() const; //Sets subsystem value - virtual void set_subsystem(uint16_t subsystem) = 0; + void set_subsystem(uint16_t subsystem); //Returns true if image has console subsystem bool is_console() const; //Returns true if image has Windows GUI subsystem bool is_gui() const; //Sets required operation system version - virtual void set_os_version(uint16_t major, uint16_t minor) = 0; + void set_os_version(uint16_t major, uint16_t minor); //Returns required operation system version (minor word) - virtual uint16_t get_minor_os_version() const = 0; + uint16_t get_minor_os_version() const; //Returns required operation system version (major word) - virtual uint16_t get_major_os_version() const = 0; + uint16_t get_major_os_version() const; //Sets required subsystem version - virtual void set_subsystem_version(uint16_t major, uint16_t minor) = 0; + void set_subsystem_version(uint16_t major, uint16_t minor); //Returns required subsystem version (minor word) - virtual uint16_t get_minor_subsystem_version() const = 0; + uint16_t get_minor_subsystem_version() const; //Returns required subsystem version (major word) - virtual uint16_t get_major_subsystem_version() const = 0; + uint16_t get_major_subsystem_version() const; public: //PE HEADER //Returns DOS header const pe_win::image_dos_header& get_dos_header() const; pe_win::image_dos_header& get_dos_header(); - //returns PE header start (e_lfanew) + //Returns PE header start (e_lfanew) int32_t get_pe_header_start() const; //Returns file alignment - virtual uint32_t get_file_alignment() const = 0; + uint32_t get_file_alignment() const; //Sets file alignment, checking the correctness of its value void set_file_alignment(uint32_t alignment); //Returns size of image - virtual uint32_t get_size_of_image() const = 0; + uint32_t get_size_of_image() const; //Returns image entry point - virtual uint32_t get_ep() const = 0; + uint32_t get_ep() const; //Sets image entry point (just a value of PE header) - virtual void set_ep(uint32_t new_ep) = 0; + void set_ep(uint32_t new_ep); //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) - virtual uint32_t get_number_of_rvas_and_sizes() const = 0; + uint32_t get_number_of_rvas_and_sizes() const; //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) - virtual void set_number_of_rvas_and_sizes(uint32_t number) = 0; + void set_number_of_rvas_and_sizes(uint32_t number); //Returns PE characteristics - virtual uint16_t get_characteristics() const = 0; + uint16_t get_characteristics() const; //Sets PE characteristics (a value inside header) - virtual void set_characteristics(uint16_t ch) = 0; + void set_characteristics(uint16_t ch); //Clears PE characteristics flag void clear_characteristics_flags(uint16_t flags); //Sets PE characteristics flag @@ -304,101 +162,124 @@ public: //PE HEADER bool check_characteristics_flag(uint16_t flag) const; //Returns DLL Characteristics - virtual uint16_t get_dll_characteristics() const = 0; + uint16_t get_dll_characteristics() const; //Sets DLL Characteristics - virtual void set_dll_characteristics(uint16_t characteristics) = 0; + void set_dll_characteristics(uint16_t characteristics); //Returns size of headers - virtual uint32_t get_size_of_headers() const = 0; + uint32_t get_size_of_headers() const; //Returns size of optional header - virtual uint16_t get_size_of_optional_header() const = 0; + uint16_t get_size_of_optional_header() const; //Returns PE signature - virtual uint32_t get_pe_signature() const = 0; + uint32_t get_pe_signature() const; //Returns magic value - virtual uint32_t get_magic() const = 0; + uint32_t get_magic() const; //Returns image base for PE32 and PE64 respectively - virtual uint32_t get_image_base_32() const = 0; + uint32_t get_image_base_32() const; void get_image_base(uint32_t& base) const; //Sets image base for PE32 and PE64 respectively - virtual uint64_t get_image_base_64() const = 0; + uint64_t get_image_base_64() const; void get_image_base(uint64_t& base) const; //Sets new image base - virtual void set_image_base(uint32_t base) = 0; - virtual void set_image_base_64(uint64_t base) = 0; + void set_image_base(uint32_t base); + void set_image_base_64(uint64_t base); //Sets heap size commit for PE32 and PE64 respectively - virtual void set_heap_size_commit(uint32_t size) = 0; - virtual void set_heap_size_commit(uint64_t size) = 0; + void set_heap_size_commit(uint32_t size); + void set_heap_size_commit(uint64_t size); //Sets heap size reserve for PE32 and PE64 respectively - virtual void set_heap_size_reserve(uint32_t size) = 0; - virtual void set_heap_size_reserve(uint64_t size) = 0; + void set_heap_size_reserve(uint32_t size); + void set_heap_size_reserve(uint64_t size); //Sets stack size commit for PE32 and PE64 respectively - virtual void set_stack_size_commit(uint32_t size) = 0; - virtual void set_stack_size_commit(uint64_t size) = 0; + void set_stack_size_commit(uint32_t size); + void set_stack_size_commit(uint64_t size); //Sets stack size reserve for PE32 and PE64 respectively - virtual void set_stack_size_reserve(uint32_t size) = 0; - virtual void set_stack_size_reserve(uint64_t size) = 0; + void set_stack_size_reserve(uint32_t size); + void set_stack_size_reserve(uint64_t size); //Returns heap size commit for PE32 and PE64 respectively - virtual uint32_t get_heap_size_commit_32() const = 0; + uint32_t get_heap_size_commit_32() const; void get_heap_size_commit(uint32_t& size) const; - virtual uint64_t get_heap_size_commit_64() const = 0; + uint64_t get_heap_size_commit_64() const; void get_heap_size_commit(uint64_t& size) const; //Returns heap size reserve for PE32 and PE64 respectively - virtual uint32_t get_heap_size_reserve_32() const = 0; + uint32_t get_heap_size_reserve_32() const; void get_heap_size_reserve(uint32_t& size) const; - virtual uint64_t get_heap_size_reserve_64() const = 0; + uint64_t get_heap_size_reserve_64() const; void get_heap_size_reserve(uint64_t& size) const; //Returns stack size commit for PE32 and PE64 respectively - virtual uint32_t get_stack_size_commit_32() const = 0; + uint32_t get_stack_size_commit_32() const; void get_stack_size_commit(uint32_t& size) const; - virtual uint64_t get_stack_size_commit_64() const = 0; + uint64_t get_stack_size_commit_64() const; void get_stack_size_commit(uint64_t& size) const; //Returns stack size reserve for PE32 and PE64 respectively - virtual uint32_t get_stack_size_reserve_32() const = 0; + uint32_t get_stack_size_reserve_32() const; void get_stack_size_reserve(uint32_t& size) const; - virtual uint64_t get_stack_size_reserve_64() const = 0; + uint64_t get_stack_size_reserve_64() const; void get_stack_size_reserve(uint64_t& size) const; //Updates virtual size of image corresponding to section virtual sizes void update_image_size(); //Returns checksum of PE file from header - virtual uint32_t get_checksum() const = 0; + uint32_t get_checksum() const; //Sets checksum of PE file - virtual void set_checksum(uint32_t checksum) = 0; + void set_checksum(uint32_t checksum); //Returns timestamp of PE file from header - virtual uint32_t get_time_date_stamp() const = 0; + uint32_t get_time_date_stamp() const; //Sets timestamp of PE file - virtual void set_time_date_stamp(uint32_t timestamp) = 0; + void set_time_date_stamp(uint32_t timestamp); //Returns Machine field value of PE file from header - virtual uint16_t get_machine() const = 0; + uint16_t get_machine() const; //Sets Machine field value of PE file - virtual void set_machine(uint16_t machine) = 0; + void set_machine(uint16_t machine); //Returns data from the beginning of image //Size = SizeOfHeaders const std::string& get_full_headers_data() const; - + + typedef std::multimap<uint32_t, std::string> debug_data_list; + //Returns raw list of debug data + const debug_data_list& get_raw_debug_data_list() const; + + //Reads and checks DOS header + static void read_dos_header(std::istream& file, pe_win::image_dos_header& header); + + //Returns sizeof() nt headers + uint32_t get_sizeof_nt_header() const; + //Returns sizeof() optional headers + uint32_t get_sizeof_opt_headers() const; + //Returns raw nt headers data pointer + const char* get_nt_headers_ptr() const; + + //Sets size of headers (to NT headers) + void set_size_of_headers(uint32_t size); + //Sets size of optional headers (to NT headers) + void set_size_of_optional_header(uint16_t size); + + //Sets base of code + void set_base_of_code(uint32_t base); + //Returns base of code + uint32_t get_base_of_code() const; public: //ADDRESS CONVERTIONS //Virtual Address (VA) to Relative Virtual Address (RVA) convertions //for PE32 and PE64 respectively //bound_check checks integer overflow - virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const = 0; - virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const = 0; + uint32_t va_to_rva(uint32_t va, bool bound_check = true) const; + uint32_t va_to_rva(uint64_t va, bool bound_check = true) const; //Relative Virtual Address (RVA) to Virtual Address (VA) convertions //for PE32 and PE64 respectively - virtual uint32_t rva_to_va_32(uint32_t rva) const = 0; + uint32_t rva_to_va_32(uint32_t rva) const; void rva_to_va(uint32_t rva, uint32_t& va) const; - virtual uint64_t rva_to_va_64(uint32_t rva) const = 0; + uint64_t rva_to_va_64(uint32_t rva) const; void rva_to_va(uint32_t rva, uint64_t& va) const; //RVA to RAW file offset convertion (4gb max) @@ -410,20 +291,14 @@ public: //ADDRESS CONVERTIONS static uint32_t rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start); public: //IMAGE SECTIONS - typedef std::vector<pe_base::section> section_list; - - //Enumeration of section data types, used in functions below - enum section_data_type - { - section_data_raw, - section_data_virtual - }; + //Returns number of sections from PE header + uint16_t get_number_of_sections() const; - //Returns number of sections - virtual uint16_t get_number_of_sections() const = 0; + //Updates number of sections in PE header + uint16_t update_number_of_sections(); //Returns section alignment - virtual uint32_t get_section_alignment() const = 0; + uint32_t get_section_alignment() const; //Returns section list section_list& get_image_sections(); @@ -458,7 +333,7 @@ public: //IMAGE SECTIONS uint32_t section_data_length_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; //Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds) - static uint32_t section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype = section_data_raw); + uint32_t section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype = section_data_raw) const; //Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 and PE64 respectively (checks bounds) uint32_t section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype = section_data_raw) const; uint32_t section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype = section_data_raw) const; @@ -482,9 +357,8 @@ public: //IMAGE SECTIONS const char* section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; //Returns corresponding section data pointer from RVA inside section "s" (checks bounds) - static char* section_data_from_rva(section& s, uint32_t rva); - static const char* section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw); - + char* section_data_from_rva(section& s, uint32_t rva); + const char* section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const; //Returns corresponding section data pointer from VA inside section "s" for PE32 and PE64 respectively (checks bounds) char* section_data_from_va(section& s, uint32_t va); //Always returns raw data const char* section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const; @@ -493,16 +367,16 @@ public: //IMAGE SECTIONS //Returns corresponding section data pointer from RVA inside section "s" (checks bounds, checks sizes, the most safe function) template<typename T> - static T section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) + T section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const { - if(rva >= s.header_.VirtualAddress && rva < s.header_.VirtualAddress + s.virtual_size_aligned_ && is_sum_safe(rva, sizeof(T))) + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) && pe_utils::is_sum_safe(rva, sizeof(T))) { - const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(); + const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment()); //Don't check for underflow here, comparsion is unsigned - if(data.size() < rva - s.header_.VirtualAddress + sizeof(T)) + if(data.size() < rva - s.get_virtual_address() + sizeof(T)) throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists); - return *reinterpret_cast<const T*>(data.data() + rva - s.header_.VirtualAddress); + return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address()); } throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); @@ -514,27 +388,27 @@ public: //IMAGE SECTIONS T section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const { //if RVA is inside of headers and we're searching them too... - if(include_headers && is_sum_safe(rva, sizeof(T)) && (rva + sizeof(T) < full_headers_data_.length())) + if(include_headers && pe_utils::is_sum_safe(rva, sizeof(T)) && (rva + sizeof(T) < full_headers_data_.length())) return *reinterpret_cast<const T*>(&full_headers_data_[rva]); const section& s = section_from_rva(rva); - const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(); + const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment()); //Don't check for underflow here, comparsion is unsigned - if(data.size() < rva - s.header_.VirtualAddress + sizeof(T)) + if(data.size() < rva - s.get_virtual_address() + sizeof(T)) throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists); - return *reinterpret_cast<const T*>(data.data() + rva - s.header_.VirtualAddress); + return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address()); } //Returns corresponding section data pointer from VA inside section "s" (checks bounds, checks sizes, the most safe function) template<typename T> - static T section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) + T section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const { return section_data_from_rva<T>(s, va_to_rva(va), datatype); } template<typename T> - static T section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) + T section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const { return section_data_from_rva<T>(s, va_to_rva(va), datatype); } @@ -587,1601 +461,63 @@ public: //IMAGE SECTIONS public: //IMAGE - //Enumeration of PE types - enum pe_type - { - pe_type_32, - pe_type_64 - }; - //Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks) static pe_type get_pe_type(std::istream& file); //Returns PE type of this image - virtual pe_type get_pe_type() const = 0; + pe_type get_pe_type() const; //Returns true if image has overlay data at the end of file bool has_overlay() const; - //Calculate checksum of image (performs no checks on PE structures) - static uint32_t calculate_checksum(std::istream& file); - - //Rebuilds PE image. If strip_dos_header == true, DOS header will be stripped a little - //If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically - void rebuild_pe(bool strip_dos_header = false, bool change_size_of_headers = true); - //Rebuilds PE image, writes resulting image to ostream "out". If strip_dos_header == true, DOS header will be stripped a little - //If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically - void rebuild_pe(std::ostream& out, bool strip_dos_header = false, bool change_size_of_headers = true); - //Realigns file (changes file alignment) void realign_file(uint32_t new_file_alignment); - -public: //EXPORTS - //Structure representing exported function - struct exported_function - { - public: - //Default constructor - exported_function(); - - //Returns ordinal of function (actually, ordinal = hint + ordinal base) - uint16_t get_ordinal() const; - - //Returns RVA of function - uint32_t get_rva() const; - - //Returns true if function has name and name ordinal - bool has_name() const; - //Returns name of function - const std::string& get_name() const; - //Returns name ordinal of function - uint16_t get_name_ordinal() const; - - //Returns true if function is forwarded to other library - bool is_forwarded() const; - //Returns the name of forwarded function - const std::string& get_forwarded_name() const; - - public: //Setters do not change everything inside image, they are used by PE class - //You can also use them to rebuild export directory - - //Sets ordinal of function - void set_ordinal(uint16_t ordinal); - - //Sets RVA of function - void set_rva(uint32_t rva); - - //Sets name of function (or clears it, if empty name is passed) - void set_name(const std::string& name); - //Sets name ordinal - void set_name_ordinal(uint16_t name_ordinal); - - //Sets forwarded function name (or clears it, if empty name is passed) - void set_forwarded_name(const std::string& name); - - private: - uint16_t ordinal_; //Function ordinal - uint32_t rva_; //Function RVA - std::string name_; //Function name - bool has_name_; //true == function has name - uint16_t name_ordinal_; //Function name ordinal - bool forward_; //true == function is forwarded - std::string forward_name_; //Name of forwarded function - }; - - //Structure representing export information - struct export_info - { - public: - //Default constructor - export_info(); - - //Returns characteristics - uint32_t get_characteristics() const; - //Returns timestamp - uint32_t get_timestamp() const; - //Returns major version - uint16_t get_major_version() const; - //Returns minor version - uint16_t get_minor_version() const; - //Returns DLL name - const std::string& get_name() const; - //Returns ordinal base - uint32_t get_ordinal_base() const; - //Returns number of functions - uint32_t get_number_of_functions() const; - //Returns number of function names - uint32_t get_number_of_names() const; - //Returns RVA of function address table - uint32_t get_rva_of_functions() const; - //Returns RVA of function name address table - uint32_t get_rva_of_names() const; - //Returns RVA of name ordinals table - uint32_t get_rva_of_name_ordinals() const; - - public: //Setters do not change everything inside image, they are used by PE class - //You can also use them to rebuild export directory using rebuild_exports - - //Sets characteristics - void set_characteristics(uint32_t characteristics); - //Sets timestamp - void set_timestamp(uint32_t timestamp); - //Sets major version - void set_major_version(uint16_t major_version); - //Sets minor version - void set_minor_version(uint16_t minor_version); - //Sets DLL name - void set_name(const std::string& name); - //Sets ordinal base - void set_ordinal_base(uint32_t ordinal_base); - //Sets number of functions - void set_number_of_functions(uint32_t number_of_functions); - //Sets number of function names - void set_number_of_names(uint32_t number_of_names); - //Sets RVA of function address table - void set_rva_of_functions(uint32_t rva_of_functions); - //Sets RVA of function name address table - void set_rva_of_names(uint32_t rva_of_names); - //Sets RVA of name ordinals table - void set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals); - - private: - uint32_t characteristics_; - uint32_t timestamp_; - uint16_t major_version_; - uint16_t minor_version_; - std::string name_; - uint32_t ordinal_base_; - uint32_t number_of_functions_; - uint32_t number_of_names_; - uint32_t address_of_functions_; - uint32_t address_of_names_; - uint32_t address_of_name_ordinals_; - }; - - -public: //EXPORTS - typedef std::vector<exported_function> exported_functions_list; - - //Returns array of exported functions - const exported_functions_list get_exported_functions() const; - //Returns array of exported functions and information about export - const exported_functions_list get_exported_functions(export_info& info) const; - //Helper export functions - //Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> - static const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports); - - //Checks if exported function name already exists - static bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports); - - //Checks if exported function ordinal already exists - static bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports); - - //Export directory rebuilder - //info - export information - //exported_functions_list - list of exported functions - //exports_section - section where export directory will be placed (must be attached to PE image) - //offset_from_section_start - offset from exports_section raw data start - //save_to_pe_headers - if true, new export directory information will be saved to PE image headers - //auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped - //number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently - //characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure - //Returns new export directory information - //exported_functions_list is copied intentionally to be sorted by ordinal values later - //Name ordinals in exported function doesn't matter, they will be recalculated - const image_directory rebuild_exports(const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); - - -public: //IMPORTS - //Structure representing imported function - struct imported_function - { - public: - //Default constructor - imported_function(); - - //Returns true if imported function has name (and hint) - bool has_name() const; - //Returns name of function - const std::string& get_name() const; - //Returns hint - uint16_t get_hint() const; - //Returns ordinal of function - uint16_t get_ordinal() const; - - //Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) - uint64_t get_iat_va() const; - - public: //Setters do not change everything inside image, they are used by PE class - //You also can use them to rebuild image imports - //Sets name of function - void set_name(const std::string& name); - //Sets hint - void set_hint(uint16_t hint); - //Sets ordinal - void set_ordinal(uint16_t ordinal); - - //Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) - void set_iat_va(uint64_t rva); - - private: - std::string name_; //Function name - uint16_t hint_; //Hint - uint16_t ordinal_; //Ordinal - uint64_t iat_va_; - }; - - //Structure representing imported library information - struct import_library - { - public: - typedef std::vector<imported_function> imported_list; - - public: - //Default constructor - import_library(); - - //Returns name of library - const std::string& get_name() const; - //Returns RVA to Import Address Table (IAT) - uint32_t get_rva_to_iat() const; - //Returns RVA to Original Import Address Table (Original IAT) - uint32_t get_rva_to_original_iat() const; - //Returns timestamp - uint32_t get_timestamp() const; - - //Returns imported functions list - const imported_list& get_imported_functions() const; - - public: //Setters do not change everything inside image, they are used by PE class - //You also can use them to rebuild image imports - //Sets name of library - void set_name(const std::string& name); - //Sets RVA to Import Address Table (IAT) - void set_rva_to_iat(uint32_t rva_to_iat); - //Sets RVA to Original Import Address Table (Original IAT) - void set_rva_to_original_iat(uint32_t rva_to_original_iat); - //Sets timestamp - void set_timestamp(uint32_t timestamp); - - //Adds imported function - void add_import(const imported_function& func); - //Clears imported functions list - void clear_imports(); - - private: - std::string name_; //Library name - uint32_t rva_to_iat_; //RVA to IAT - uint32_t rva_to_original_iat_; //RVA to original IAT - uint32_t timestamp_; //DLL TimeStamp - - imported_list imports_; - }; - - typedef std::vector<import_library> imported_functions_list; - - //Returns imported functions list with related libraries info - virtual const imported_functions_list get_imported_functions() const = 0; - - - //Simple import directory rebuilder - //Structure representing import rebuilder advanced settings - struct import_rebuilder_settings - { - public: - //Default constructor - //Default constructor - //If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset - //to new value after import rebuilding - //If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero - //IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable - //to be able to modify IAT thunks - explicit import_rebuilder_settings(bool set_to_pe_headers = true, bool auto_zero_directory_entry_iat = false); - - //Returns offset from section start where import directory data will be placed - uint32_t get_offset_from_section_start() const; - //Returns true if Original import address table (IAT) will be rebuilt - bool build_original_iat() const; - - //Returns true if Original import address and import address tables will not be rebuilt, - //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero - bool save_iat_and_original_iat_rvas() const; - //Returns true if Original import address and import address tables contents will be rewritten - //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero - //and save_iat_and_original_iat_rvas is true - bool rewrite_iat_and_original_iat_contents() const; - - //Returns true if original missing IATs will be rebuilt - //(only if IATs are saved) - bool fill_missing_original_iats() const; - //Returns true if PE headers should be updated automatically after rebuilding of imports - bool auto_set_to_pe_headers() const; - //Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true - bool zero_directory_entry_iat() const; - - //Returns true if the last section should be stripped automatically, if imports are inside it - bool auto_strip_last_section_enabled() const; - - - public: //Setters - //Sets offset from section start where import directory data will be placed - void set_offset_from_section_start(uint32_t offset); - //Sets if Original import address table (IAT) will be rebuilt - void build_original_iat(bool enable); - //Sets if Original import address and import address tables will not be rebuilt, - //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero - //enable_rewrite_iat_and_original_iat_contents sets if Original import address and import address tables contents will be rewritten - //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero - //and save_iat_and_original_iat_rvas is true - void save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents = false); - //Sets if original missing IATs will be rebuilt - //(only if IATs are saved) - void fill_missing_original_iats(bool enable); - //Sets if PE headers should be updated automatically after rebuilding of imports - void auto_set_to_pe_headers(bool enable); - //Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true - void zero_directory_entry_iat(bool enable); - - //Sets if the last section should be stripped automatically, if imports are inside it, default true - void enable_auto_strip_last_section(bool enable); - - private: - uint32_t offset_from_section_start_; - bool build_original_iat_; - bool save_iat_and_original_iat_rvas_; - bool fill_missing_original_iats_; - bool set_to_pe_headers_; - bool zero_directory_entry_iat_; - bool rewrite_iat_and_original_iat_contents_; - bool auto_strip_last_section_; - }; - - //You can get all image imports with get_imported_functions() function - //You can use returned value to, for example, add new imported library with some functions - //to the end of list of imported libraries - //To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default) - //Don't add new imported functions to existing imported library entries, because this can cause - //rewriting of some used memory (or other IAT/orig.IAT fields) by system loader - //The safest way is just adding import libraries with functions to the end of imported_functions_list array - virtual const image_directory rebuild_imports(const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings()) = 0; - - -public: //RELOCATIONS - //Structure representing relocation entry - //RVA of relocation is not actually RVA, but - //(real RVA) - (RVA of table) - struct relocation_entry - { - public: - //Default constructor - relocation_entry(); - //Constructor from relocation item (WORD) - explicit relocation_entry(uint16_t relocation_value); - //Constructor from relative rva and relocation type - relocation_entry(uint16_t rrva, uint16_t type); - - //Returns RVA of relocation (actually, relative RVA from relocation table RVA) - uint16_t get_rva() const; - //Returns type of relocation - uint16_t get_type() const; - - //Returns relocation item (rrva + type) - uint16_t get_item() const; - - public: //Setters do not change everything inside image, they are used by PE class - //You can also use them to rebuild relocations using rebuild_relocations() - - //Sets RVA of relocation (actually, relative RVA from relocation table RVA) - void set_rva(uint16_t rva); - //Sets type of relocation - void set_type(uint16_t type); - - //Sets relocation item (rrva + type) - void set_item(uint16_t item); - - private: - uint16_t rva_; - uint16_t type_; - }; - - //Structure representing relocation table - struct relocation_table - { - public: - typedef std::vector<relocation_entry> relocation_list; - - public: - //Default constructor - relocation_table(); - //Constructor from RVA of relocation table - explicit relocation_table(uint32_t rva); - - //Returns relocation list - const relocation_list& get_relocations() const; - //Returns RVA of block - uint32_t get_rva() const; - - public: //These functions do not change everything inside image, they are used by PE class - //You can also use them to rebuild relocations using rebuild_relocations() - - //Adds relocation to table - void add_relocation(const relocation_entry& entry); - //Returns changeable relocation list - relocation_list& get_relocations(); - //Sets RVA of block - void set_rva(uint32_t rva); - - private: - uint32_t rva_; - relocation_list relocations_; - }; - - typedef std::vector<relocation_table> relocation_table_list; - - //Get relocation list of pe file, supports one-word sized relocations only - //If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed - const relocation_table_list get_relocations(bool list_absolute_entries = false) const; - - //Simple relocations rebuilder - //To keep PE file working, don't remove any of existing relocations in - //relocation_table_list returned by a call to get_relocations() function - //auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped - //offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated - //If save_to_pe_header is true, PE header will be modified automatically - const image_directory rebuild_relocations(const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); - - //Recalculates image base with the help of relocation tables - //Recalculates VAs of DWORDS/QWORDS in image according to relocations - //Notice: if you move some critical structures like TLS, image relocations will not fix new - //positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed. - //It is recommended to rebase image in the very beginning and move all structures afterwards. - virtual void rebase_image(const relocation_table_list& tables, uint64_t new_base) = 0; - - -public: //TLS - //Structure representing TLS info - //We use "DWORD" type to represent RVAs, because RVA is - //always 32bit even in PE+ - struct tls_info - { - public: - typedef std::vector<uint32_t> tls_callback_list; - - public: - //Default constructor - tls_info(); - - //Returns start RVA of TLS raw data - uint32_t get_raw_data_start_rva() const; - //Returns end RVA of TLS raw data - uint32_t get_raw_data_end_rva() const; - //Returns TLS index RVA - uint32_t get_index_rva() const; - //Returns TLS callbacks RVA - uint32_t get_callbacks_rva() const; - //Returns size of zero fill - uint32_t get_size_of_zero_fill() const; - //Returns characteristics - uint32_t get_characteristics() const; - //Returns raw TLS data - const std::string& get_raw_data() const; - //Returns TLS callbacks addresses - const tls_callback_list& get_tls_callbacks() const; - - public: //These functions do not change everything inside image, they are used by PE class - //You can also use them to rebuild TLS directory - - //Sets start RVA of TLS raw data - void set_raw_data_start_rva(uint32_t rva); - //Sets end RVA of TLS raw data - void set_raw_data_end_rva(uint32_t rva); - //Sets TLS index RVA - void set_index_rva(uint32_t rva); - //Sets TLS callbacks RVA - void set_callbacks_rva(uint32_t rva); - //Sets size of zero fill - void set_size_of_zero_fill(uint32_t size); - //Sets characteristics - void set_characteristics(uint32_t characteristics); - //Sets raw TLS data - void set_raw_data(const std::string& data); - //Returns TLS callbacks addresses - tls_callback_list& get_tls_callbacks(); - //Adds TLS callback - void add_tls_callback(uint32_t rva); - //Clears TLS callbacks list - void clear_tls_callbacks(); - //Recalculates end address of raw TLS data - void recalc_raw_data_end_rva(); - - private: - uint32_t start_rva_, end_rva_, index_rva_, callbacks_rva_; - uint32_t size_of_zero_fill_, characteristics_; - - //Raw TLS data - std::string raw_data_; - - //TLS callback RVAs - tls_callback_list callbacks_; - }; - - //Get TLS info - //If image does not have TLS, throws an exception - virtual const tls_info get_tls_info() const = 0; - - //Rebuilder of TLS structures - - //Represents type of expanding of TLS section containing raw data - //(Works only if you are writing TLS raw data to tls_section and it is the last one in the PE image on the moment of TLS rebuild) - enum tls_data_expand_type - { - tls_data_expand_raw, //If there is not enough raw space for raw TLS data, it can be expanded - tls_data_expand_virtual //If there is not enough virtual place for raw TLS data, it can be expanded - }; - - //If write_tls_callbacks = true, TLS callbacks VAs will be written to their place - //If write_tls_data = true, TLS data will be written to its place - //If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string - //representing raw data content - //auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped - virtual const image_directory rebuild_tls(const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true) = 0; - - -public: //IMAGE CONFIG - //Structure representing image configuration information - struct image_config_info - { - public: - typedef std::vector<uint32_t> se_handler_list; - typedef std::vector<uint32_t> lock_prefix_rva_list; - - public: - //Default constructor - image_config_info(); - //Constructors from PE structures (no checks) - template<typename ConfigStructure> - explicit image_config_info(const ConfigStructure& info); - - //Returns the date and time stamp value - uint32_t get_time_stamp() const; - //Returns major version number - uint16_t get_major_version() const; - //Returns minor version number - uint16_t get_minor_version() const; - //Returns clear global flags - uint32_t get_global_flags_clear() const; - //Returns set global flags - uint32_t get_global_flags_set() const; - //Returns critical section default timeout - uint32_t get_critical_section_default_timeout() const; - //Get the size of the minimum block that - //must be freed before it is freed (de-committed), in bytes - uint64_t get_decommit_free_block_threshold() const; - //Returns the size of the minimum total memory - //that must be freed in the process heap before it is freed (de-committed), in bytes - uint64_t get_decommit_total_free_threshold() const; - //Returns VA of a list of addresses where the LOCK prefix is used - uint64_t get_lock_prefix_table_va() const; - //Returns the maximum allocation size, in bytes - uint64_t get_max_allocation_size() const; - //Returns the maximum block size that can be allocated from heap segments, in bytes - uint64_t get_virtual_memory_threshold() const; - //Returns process affinity mask - uint64_t get_process_affinity_mask() const; - //Returns process heap flags - uint32_t get_process_heap_flags() const; - //Returns service pack version (CSDVersion) - uint16_t get_service_pack_version() const; - //Returns VA of edit list (reserved by system) - uint64_t get_edit_list_va() const; - //Returns a pointer to a cookie that is used by Visual C++ or GS implementation - uint64_t get_security_cookie_va() const; - //Returns VA of the sorted table of RVAs of each valid, unique handler in the image - uint64_t get_se_handler_table_va() const; - //Returns the count of unique handlers in the table - uint64_t get_se_handler_count() const; - - //Returns SE Handler RVA list - const se_handler_list& get_se_handler_rvas() const; - - //Returns Lock Prefix RVA list - const lock_prefix_rva_list& get_lock_prefix_rvas() const; - - public: //These functions do not change everything inside image, they are used by PE class - //Also you can use these functions to rebuild image config directory - - //Adds SE Handler RVA to list - void add_se_handler_rva(uint32_t rva); - //Clears SE Handler list - void clear_se_handler_list(); - - //Adds Lock Prefix RVA to list - void add_lock_prefix_rva(uint32_t rva); - //Clears Lock Prefix list - void clear_lock_prefix_list(); - - //Sets the date and time stamp value - void set_time_stamp(uint32_t time_stamp); - //Sets major version number - void set_major_version(uint16_t major_version); - //Sets minor version number - void set_minor_version(uint16_t minor_version); - //Sets clear global flags - void set_global_flags_clear(uint32_t global_flags_clear); - //Sets set global flags - void set_global_flags_set(uint32_t global_flags_set); - //Sets critical section default timeout - void set_critical_section_default_timeout(uint32_t critical_section_default_timeout); - //Sets the size of the minimum block that - //must be freed before it is freed (de-committed), in bytes - void set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold); - //Sets the size of the minimum total memory - //that must be freed in the process heap before it is freed (de-committed), in bytes - void set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold); - //Sets VA of a list of addresses where the LOCK prefix is used - //If you rebuild this list, VA will be re-assigned automatically - void set_lock_prefix_table_va(uint64_t lock_prefix_table_va); - //Sets the maximum allocation size, in bytes - void set_max_allocation_size(uint64_t max_allocation_size); - //Sets the maximum block size that can be allocated from heap segments, in bytes - void set_virtual_memory_threshold(uint64_t virtual_memory_threshold); - //Sets process affinity mask - void set_process_affinity_mask(uint64_t process_affinity_mask); - //Sets process heap flags - void set_process_heap_flags(uint32_t process_heap_flags); - //Sets service pack version (CSDVersion) - void set_service_pack_version(uint16_t service_pack_version); - //Sets VA of edit list (reserved by system) - void set_edit_list_va(uint64_t edit_list_va); - //Sets a pointer to a cookie that is used by Visual C++ or GS implementation - void set_security_cookie_va(uint64_t security_cookie_va); - //Sets VA of the sorted table of RVAs of each valid, unique handler in the image - //If you rebuild this list, VA will be re-assigned automatically - void set_se_handler_table_va(uint64_t se_handler_table_va); - - //Returns SE Handler RVA list - se_handler_list& get_se_handler_rvas(); - - //Returns Lock Prefix RVA list - lock_prefix_rva_list& get_lock_prefix_rvas(); - - private: - uint32_t time_stamp_; - uint16_t major_version_, minor_version_; - uint32_t global_flags_clear_, global_flags_set_; - uint32_t critical_section_default_timeout_; - uint64_t decommit_free_block_threshold_, decommit_total_free_threshold_; - uint64_t lock_prefix_table_va_; - uint64_t max_allocation_size_; - uint64_t virtual_memory_threshold_; - uint64_t process_affinity_mask_; - uint32_t process_heap_flags_; - uint16_t service_pack_version_; - uint64_t edit_list_va_; - uint64_t security_cookie_va_; - uint64_t se_handler_table_va_; - uint64_t se_handler_count_; - - se_handler_list se_handlers_; - lock_prefix_rva_list lock_prefixes_; - }; - - //Returns image config info - //If image does not have config info, throws an exception - virtual const image_config_info get_image_config() const = 0; - - - //Image config directory rebuilder - //auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped - //If write_se_handlers = true, SE Handlers list will be written just after image config directory structure - //If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure - virtual const image_directory rebuild_image_config(const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true) = 0; - - -public: //BOUND IMPORT - //Structure representing bound import reference - struct bound_import_ref - { - public: - //Default constructor - bound_import_ref(); - //Constructor from data - bound_import_ref(const std::string& module_name, uint32_t timestamp); - - //Returns imported module name - const std::string& get_module_name() const; - //Returns bound import date and time stamp - uint32_t get_timestamp() const; - - private: - std::string module_name_; //Imported module name - uint32_t timestamp_; //Bound import timestamp - }; - - //Structure representing image bound import information - struct bound_import - { - public: - typedef std::vector<bound_import_ref> ref_list; - - public: - //Default constructor - bound_import(); - //Constructor from data - bound_import(const std::string& module_name, uint32_t timestamp); - - //Returns imported module name - const std::string& get_module_name() const; - //Returns bound import date and time stamp - uint32_t get_timestamp() const; - - //Returns bound references cound - size_t get_module_ref_count() const; - //Returns module references - const ref_list& get_module_ref_list() const; - - public: //These functions do not change everything inside image, they are used by PE class - //Adds module reference - void add_module_ref(const bound_import_ref& ref); - //Clears module references list - void clear_module_refs(); - //Returns module references - ref_list& get_module_ref_list(); - - private: - std::string module_name_; //Imported module name - uint32_t timestamp_; //Bound import timestamp - ref_list refs_; //Module references list - }; - - typedef std::vector<bound_import> bound_import_module_list; - - //Returns bound import information - const bound_import_module_list get_bound_import_module_list() const; - - -public: //RESOURCES - //Structure representing resource data entry - struct resource_data_entry - { - public: - //Default constructor - resource_data_entry(); - //Constructor from data - resource_data_entry(const std::string& data, uint32_t codepage); - - //Returns resource data codepage - uint32_t get_codepage() const; - //Returns resource data - const std::string& get_data() const; - - public: //These functions do not change everything inside image, they are used by PE class - //You can also use them to rebuild resource directory - - //Sets resource data codepage - void set_codepage(uint32_t codepage); - //Sets resource data - void set_data(const std::string& data); - - private: - uint32_t codepage_; //Resource data codepage - std::string data_; //Resource data - }; - - //Forward declaration - struct resource_directory; - - //Structure representing resource directory entry - struct resource_directory_entry - { - public: - //Default constructor - resource_directory_entry(); - //Copy constructor - resource_directory_entry(const resource_directory_entry& other); - //Copy assignment operator - resource_directory_entry& operator=(const resource_directory_entry& other); - - //Returns entry ID - uint32_t get_id() const; - //Returns entry name - const std::wstring& get_name() const; - //Returns true, if entry has name - //Returns false, if entry has ID - bool is_named() const; - - //Returns true, if entry includes resource_data_entry - //Returns false, if entry includes resource_directory - bool includes_data() const; - //Returns resource_directory if entry includes it, otherwise throws an exception - const resource_directory& get_resource_directory() const; - //Returns resource_data_entry if entry includes it, otherwise throws an exception - const resource_data_entry& get_data_entry() const; - - //Destructor - ~resource_directory_entry(); - - public: //These functions do not change everything inside image, they are used by PE class - //You can also use them to rebuild resource directory - - //Sets entry name - void set_name(const std::wstring& name); - //Sets entry ID - void set_id(uint32_t id); - - //Returns resource_directory if entry includes it, otherwise throws an exception - resource_directory& get_resource_directory(); - //Returns resource_data_entry if entry includes it, otherwise throws an exception - resource_data_entry& get_data_entry(); - - //Adds resource_data_entry - void add_data_entry(const resource_data_entry& entry); - //Adds resource_directory - void add_resource_directory(const resource_directory& dir); - - private: - //Destroys included data - void release(); - - private: - uint32_t id_; - std::wstring name_; - - union includes - { - //Default constructor - includes(); - - //We use pointers, we're doing manual copying here - struct resource_data_entry* data_; - struct resource_directory* dir_; //We use pointer, because structs include each other - }; - - includes ptr_; - - bool includes_data_, named_; - }; - - //Structure representing resource directory - struct resource_directory - { - public: - typedef std::vector<resource_directory_entry> entry_list; - - public: - //Default constructor - resource_directory(); - //Constructor from data - explicit resource_directory(const pe_win::image_resource_directory& dir); - - //Returns characteristics of directory - uint32_t get_characteristics() const; - //Returns date and time stamp of directory - uint32_t get_timestamp() const; - //Returns number of named entries - uint32_t get_number_of_named_entries() const; - //Returns number of ID entries - uint32_t get_number_of_id_entries() const; - //Returns major version of directory - uint16_t get_major_version() const; - //Returns minor version of directory - uint16_t get_minor_version() const; - //Returns resource_directory_entry array - const entry_list& get_entry_list() const; - //Returns resource_directory_entry by ID. If not found - throws an exception - const resource_directory_entry& entry_by_id(uint32_t id) const; - //Returns resource_directory_entry by name. If not found - throws an exception - const resource_directory_entry& entry_by_name(const std::wstring& name) const; - - public: //These functions do not change everything inside image, they are used by PE class - //You can also use them to rebuild resource directory - - //Adds resource_directory_entry - void add_resource_directory_entry(const resource_directory_entry& entry); - //Clears resource_directory_entry array - void clear_resource_directory_entry_list(); - - //Sets characteristics of directory - void set_characteristics(uint32_t characteristics); - //Sets date and time stamp of directory - void set_timestamp(uint32_t timestamp); - //Sets number of named entries - void set_number_of_named_entries(uint32_t number); - //Sets number of ID entries - void set_number_of_id_entries(uint32_t number); - //Sets major version of directory - void set_major_version(uint16_t major_version); - //Sets minor version of directory - void get_minor_version(uint16_t minor_version); - - //Returns resource_directory_entry array - entry_list& get_entry_list(); - - private: - uint32_t characteristics_; - uint32_t timestamp_; - uint16_t major_version_, minor_version_; - uint32_t number_of_named_entries_, number_of_id_entries_; - entry_list entries_; - - public: //Finder helpers - //Finds resource_directory_entry by ID - struct id_entry_finder - { - public: - explicit id_entry_finder(uint32_t id); - bool operator()(const resource_directory_entry& entry) const; - - private: - uint32_t id_; - }; - - //Finds resource_directory_entry by name - struct name_entry_finder - { - public: - explicit name_entry_finder(const std::wstring& name); - bool operator()(const resource_directory_entry& entry) const; - - private: - std::wstring name_; - }; - - //Finds resource_directory_entry by name or ID (universal) - struct entry_finder - { - public: - explicit entry_finder(const std::wstring& name); - explicit entry_finder(uint32_t id); - bool operator()(const resource_directory_entry& entry) const; - - private: - std::wstring name_; - uint32_t id_; - bool named_; - }; - }; - - //Returns resources (root resource_directory) from PE file - const resource_directory get_resources() const; - - //Resources rebuilder - //resource_directory - root resource directory - //resources_section - section where resource directory will be placed (must be attached to PE image) - //resource_directory is non-constant, because it will be sorted - //offset_from_section_start - offset from resources_section raw data start - //save_to_pe_headers - if true, new resource directory information will be saved to PE image headers - //auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped - //number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used - const image_directory rebuild_resources(resource_directory& info, section& resources_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); - - -public: //EXCEPTION DIRECTORY (exists on PE+ only) - //Structure representing exception directory entry - struct exception_entry - { - public: - //Default constructor - exception_entry(); - //Constructor from data - exception_entry(const pe_win::image_runtime_function_entry& entry, const pe_win::unwind_info& unwind_info); - - //Returns starting address of function, affected by exception unwinding - uint32_t get_begin_address() const; - //Returns ending address of function, affected by exception unwinding - uint32_t get_end_address() const; - //Returns unwind info address - uint32_t get_unwind_info_address() const; - - //Returns UNWIND_INFO structure version - uint8_t get_unwind_info_version() const; - - //Returns unwind info flags - uint8_t get_flags() const; - //The function has an exception handler that should be called - //when looking for functions that need to examine exceptions - bool has_exception_handler() const; - //The function has a termination handler that should be called - //when unwinding an exception - bool has_termination_handler() const; - //The unwind info structure is not the primary one for the procedure - bool is_chaininfo() const; - - //Returns size of function prolog - uint8_t get_size_of_prolog() const; - - //Returns number of unwind slots - uint8_t get_number_of_unwind_slots() const; - - //If the function uses frame pointer - bool uses_frame_pointer() const; - //Number of the nonvolatile register used as the frame pointer, - //using the same encoding for the operation info field of UNWIND_CODE nodes - uint8_t get_frame_pointer_register_number() const; - //The scaled offset from RSP that is applied to the FP reg when it is established. - //The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 - uint8_t get_scaled_rsp_offset() const; - - private: - uint32_t begin_address_, end_address_, unwind_info_address_; - uint8_t unwind_info_version_; - uint8_t flags_; - uint8_t size_of_prolog_; - uint8_t count_of_codes_; - uint8_t frame_register_, frame_offset_; - }; - - typedef std::vector<exception_entry> exception_entry_list; - - //Returns exception directory data (exists on PE+ only) - //Unwind opcodes are not listed, because their format and list are subject to change - const exception_entry_list get_exception_directory_data() const; - - -public: //DEBUG - //Structure representing advanced RSDS (PDB 7.0) information - struct pdb_7_0_info - { - public: - //Default constructor - pdb_7_0_info(); - //Constructor from data - explicit pdb_7_0_info(const pe_win::CV_INFO_PDB70* info); - - //Returns debug PDB 7.0 structure GUID - const pe_win::guid get_guid() const; - //Returns age of build - uint32_t get_age() const; - //Returns PDB file name / path - const std::string& get_pdb_file_name() const; - - private: - uint32_t age_; - pe_win::guid guid_; - std::string pdb_file_name_; - }; - - //Structure representing advanced NB10 (PDB 2.0) information - struct pdb_2_0_info - { - public: - //Default constructor - pdb_2_0_info(); - //Constructor from data - explicit pdb_2_0_info(const pe_win::CV_INFO_PDB20* info); - - //Returns debug PDB 2.0 structure signature - uint32_t get_signature() const; - //Returns age of build - uint32_t get_age() const; - //Returns PDB file name / path - const std::string& get_pdb_file_name() const; - - private: - uint32_t age_; - uint32_t signature_; - std::string pdb_file_name_; - }; - - //Structure representing advanced misc (IMAGE_DEBUG_TYPE_MISC) info - struct misc_debug_info - { - public: - //Default constructor - misc_debug_info(); - //Constructor from data - explicit misc_debug_info(const pe_win::image_debug_misc* info); - - //Returns debug data type - uint32_t get_data_type() const; - //Returns true if data type is exe name - bool is_exe_name() const; - - //Returns true if debug data is UNICODE - bool is_unicode() const; - //Returns debug data (ANSI or UNICODE) - const std::string& get_data_ansi() const; - const std::wstring& get_data_unicode() const; - - private: - uint32_t data_type_; - bool unicode_; - std::string debug_data_ansi_; - std::wstring debug_data_unicode_; - }; - - //Structure representing COFF (IMAGE_DEBUG_TYPE_COFF) debug info - struct coff_debug_info - { - public: - //Structure representing COFF symbol - struct coff_symbol - { - public: - //Default constructor - coff_symbol(); - - //Returns storage class - uint32_t get_storage_class() const; - //Returns symbol index - uint32_t get_index() const; - //Returns section number - uint32_t get_section_number() const; - //Returns RVA - uint32_t get_rva() const; - //Returns type - uint16_t get_type() const; - - //Returns true if structure contains file name - bool is_file() const; - //Returns text data (symbol or file name) - const std::string& get_symbol() const; - - public: //These functions do not change everything inside image, they are used by PE class - //Sets storage class - void set_storage_class(uint32_t storage_class); - //Sets symbol index - void set_index(uint32_t index); - //Sets section number - void set_section_number(uint32_t section_number); - //Sets RVA - void set_rva(uint32_t rva); - //Sets type - void set_type(uint16_t type); - - //Sets file name - void set_file_name(const std::string& file_name); - //Sets symbol name - void set_symbol_name(const std::string& symbol_name); - - private: - uint32_t storage_class_; - uint32_t index_; - uint32_t section_number_, rva_; - uint16_t type_; - bool is_filename_; - std::string name_; - }; - - public: - typedef std::vector<coff_symbol> coff_symbols_list; - - public: - //Default constructor - coff_debug_info(); - //Constructor from data - explicit coff_debug_info(const pe_win::image_coff_symbols_header* info); - - //Returns number of symbols - uint32_t get_number_of_symbols() const; - //Returns virtual address of the first symbol - uint32_t get_lva_to_first_symbol() const; - //Returns number of line-number entries - uint32_t get_number_of_line_numbers() const; - //Returns virtual address of the first line-number entry - uint32_t get_lva_to_first_line_number() const; - //Returns relative virtual address of the first byte of code - uint32_t get_rva_to_first_byte_of_code() const; - //Returns relative virtual address of the last byte of code - uint32_t get_rva_to_last_byte_of_code() const; - //Returns relative virtual address of the first byte of data - uint32_t get_rva_to_first_byte_of_data() const; - //Returns relative virtual address of the last byte of data - uint32_t get_rva_to_last_byte_of_data() const; - - //Returns COFF symbols list - const coff_symbols_list& get_symbols() const; - - public: //These functions do not change everything inside image, they are used by PE class - //Adds COFF symbol - void add_symbol(const coff_symbol& sym); - - private: - uint32_t number_of_symbols_; - uint32_t lva_to_first_symbol_; - uint32_t number_of_line_numbers_; - uint32_t lva_to_first_line_number_; - uint32_t rva_to_first_byte_of_code_; - uint32_t rva_to_last_byte_of_code_; - uint32_t rva_to_first_byte_of_data_; - uint32_t rva_to_last_byte_of_data_; - - private: - coff_symbols_list symbols_; - }; - - //Structure representing debug information - struct debug_info - { - public: - //Enumeration of debug information types - enum debug_info_type - { - debug_type_unknown, - debug_type_coff, - debug_type_codeview, - debug_type_fpo, - debug_type_misc, - debug_type_exception, - debug_type_fixup, - debug_type_omap_to_src, - debug_type_omap_from_src, - debug_type_borland, - debug_type_reserved10, - debug_type_clsid - }; - - public: - //Enumeration of advanced debug information types - enum advanced_info_type - { - advanced_info_none, //No advanced info - advanced_info_pdb_7_0, //PDB 7.0 - advanced_info_pdb_2_0, //PDB 2.0 - advanced_info_misc, //MISC debug info - advanced_info_coff, //COFF debug info - //No advanced info structures available for types below - advanced_info_codeview_4_0, //CodeView 4.0 - advanced_info_codeview_5_0, //CodeView 5.0 - advanced_info_codeview //CodeView - }; - - public: - //Default constructor - debug_info(); - //Constructor from data - explicit debug_info(const pe_win::image_debug_directory& debug); - //Copy constructor - debug_info(const debug_info& info); - //Copy assignment operator - debug_info& operator=(const debug_info& info); - //Destructor - ~debug_info(); - - //Returns debug characteristics - uint32_t get_characteristics() const; - //Returns debug datetimestamp - uint32_t get_time_stamp() const; - //Returns major version - uint32_t get_major_version() const; - //Returns minor version - uint32_t get_minor_version() const; - //Returns type of debug info (unchecked) - uint32_t get_type_raw() const; - //Returns type of debug info from debug_info_type enumeration - debug_info_type get_type() const; - //Returns size of debug data (internal, .pdb or other file doesn't count) - uint32_t get_size_of_data() const; - //Returns RVA of debug info when mapped to memory or zero, if info is not mapped - uint32_t get_rva_of_raw_data() const; - //Returns raw file pointer to raw data - uint32_t get_pointer_to_raw_data() const; - - //Returns advanced debug information type - advanced_info_type get_advanced_info_type() const; - //Returns advanced debug information or throws an exception, - //if requested information type is not contained by structure - template<typename AdvancedInfo> - const AdvancedInfo get_advanced_debug_info() const; - - public: //These functions do not change everything inside image, they are used by PE class - //Sets advanced debug information - void set_advanced_debug_info(const pdb_7_0_info& info); - void set_advanced_debug_info(const pdb_2_0_info& info); - void set_advanced_debug_info(const misc_debug_info& info); - void set_advanced_debug_info(const coff_debug_info& info); - - //Sets advanced debug information type, if no advanced info structure available - void set_advanced_info_type(advanced_info_type type); - - private: - uint32_t characteristics_; - uint32_t time_stamp_; - uint32_t major_version_, minor_version_; - uint32_t type_; - uint32_t size_of_data_; - uint32_t address_of_raw_data_; //RVA when mapped or 0 - uint32_t pointer_to_raw_data_; //RAW file offset - - //Union containing advanced debug information pointer - union advanced_info - { - public: - //Default constructor - advanced_info(); - - //Returns true if advanced debug info is present - bool is_present() const; - - public: - pdb_7_0_info* adv_pdb_7_0_info; - pdb_2_0_info* adv_pdb_2_0_info; - misc_debug_info* adv_misc_info; - coff_debug_info* adv_coff_info; - }; - - //Helper for advanced debug information copying - void copy_advanced_info(const debug_info& info); - //Helper for clearing any present advanced debug information - void free_present_advanced_info(); - - advanced_info advanced_debug_info_; - //Advanced information type - advanced_info_type advanced_info_type_; - }; - - typedef std::vector<debug_info> debug_info_list; - - //Returns debug information list - const debug_info_list get_debug_information() const; - - -public: //.NET - //Structure representing basic .NET header information - struct basic_dotnet_info - { - public: - //Default constructor - basic_dotnet_info(); - //Constructor from data - explicit basic_dotnet_info(const pe_win::image_cor20_header& header); - - //Returns major runtime version - uint16_t get_major_runtime_version() const; - //Returns minor runtime version - uint16_t get_minor_runtime_version() const; - - //Returns RVA of metadata (symbol table and startup information) - uint32_t get_rva_of_metadata() const; - //Returns size of metadata (symbol table and startup information) - uint32_t get_size_of_metadata() const; - - //Returns flags - uint32_t get_flags() const; - - //Returns true if entry point is native - bool is_native_entry_point() const; - //Returns true if 32 bit required - bool is_32bit_required() const; - //Returns true if image is IL library - bool is_il_library() const; - //Returns true if image uses IL only - bool is_il_only() const; - - //Returns entry point RVA (if entry point is native) - //Returns entry point managed token (if entry point is managed) - uint32_t get_entry_point_rva_or_token() const; - - //Returns RVA of managed resources - uint32_t get_rva_of_resources() const; - //Returns size of managed resources - uint32_t get_size_of_resources() const; - //Returns RVA of strong name signature - uint32_t get_rva_of_strong_name_signature() const; - //Returns size of strong name signature - uint32_t get_size_of_strong_name_signature() const; - //Returns RVA of code manager table - uint32_t get_rva_of_code_manager_table() const; - //Returns size of code manager table - uint32_t get_size_of_code_manager_table() const; - //Returns RVA of VTable fixups - uint32_t get_rva_of_vtable_fixups() const; - //Returns size of VTable fixups - uint32_t get_size_of_vtable_fixups() const; - //Returns RVA of export address table jumps - uint32_t get_rva_of_export_address_table_jumps() const; - //Returns size of export address table jumps - uint32_t get_size_of_export_address_table_jumps() const; - //Returns RVA of managed native header - //(precompiled header info, usually set to zero, for internal use) - uint32_t get_rva_of_managed_native_header() const; - //Returns size of managed native header - //(precompiled header info, usually set to zero, for internal use) - uint32_t get_size_of_managed_native_header() const; - - private: - pe_win::image_cor20_header header_; - }; - - //Returns basic .NET information - //If image is not native, throws an exception - const basic_dotnet_info get_basic_dotnet_info() const; - - -public: //ENTROPY - //Calculates entropy for PE image section - static double calculate_entropy(const section& s); - - //Calculates entropy for istream (from current position of stream) - static double calculate_entropy(std::istream& file); - - //Calculates entropy for data block - static double calculate_entropy(const char* data, size_t length); - - //Calculates entropy for this PE file (only section data) - double calculate_entropy() const; - - -public: //UTILS - //Returns true if string "data" with maximum length "raw_length" is null-terminated - template<typename T> - static bool is_null_terminated(const T* data, size_t raw_length) - { - raw_length /= sizeof(T); - for(size_t l = 0; l < raw_length; l++) - { - if(data[l] == static_cast<T>(L'\0')) - return true; - } - - return false; - } - - //Helper template function to strip nullbytes in the end of string - template<typename T> - static void strip_nullbytes(std::basic_string<T>& str) - { - while(!*(str.end() - 1) && !str.empty()) - str.erase(str.length() - 1); - } - - //Helper function to determine if number is power of 2 - template<typename T> - static inline bool is_power_of_2(T x) - { - return !(x & (x - 1)); - } - - //Helper function to align number down - template<typename T> - static inline T align_down(T x, uint32_t align) - { - return x & ~(static_cast<T>(align) - 1); - } - - //Helper function to align number up - template<typename T> - static inline T align_up(T x, uint32_t align) - { - return (x & static_cast<T>(align - 1)) ? align_down(x, align) + static_cast<T>(align) : x; - } - - //Returns true if sum of two unsigned integers is safe (no overflow occurs) - static inline bool is_sum_safe(uint32_t a, uint32_t b) - { - return a <= static_cast<uint32_t>(-1) - b; - } - - //Two gigabytes value in bytes - static const uint32_t two_gb = 0x80000000; - static const uint32_t max_dword = 0xFFFFFFFF; - static const uint32_t max_word = 0x0000FFFF; - static const double log_2; //instead of using M_LOG2E - + //Helper function to recalculate RAW and virtual section sizes and strip it, if necessary + //auto_strip = strip section, if necessary + void recalculate_section_sizes(section& s, bool auto_strip); // ========== END OF PUBLIC MEMBERS AND STRUCTURES ========== // -protected: +private: //Image DOS header pe_win::image_dos_header dos_header_; //Rich (stub) overlay data (for MSVS) std::string rich_overlay_; //List of image sections section_list sections_; - //Pointer to section data - std::size_t ptr_to_section_data_; //True if image has overlay bool has_overlay_; - //Raw bound import structures data - std::string bound_import_data_; //Raw SizeOfHeaders-sized data from the beginning of image std::string full_headers_data_; //Raw debug data for all directories //PointerToRawData; Data - typedef std::multimap<uint32_t, std::string> debug_data_list; debug_data_list debug_data_; + //PE or PE+ related properties + pe_properties* props_; //Reads and checks DOS header void read_dos_header(std::istream& file); - //Reads and checks DOS header - static void read_dos_header(std::istream& file, pe_win::image_dos_header& header); - //Returns stream size - static std::streamoff get_file_size(std::istream& file); //Reads and checks PE headers and section headers, data - void read_pe(std::istream& file, bool read_bound_import_raw_data, bool read_debug_raw_data); + void read_pe(std::istream& file, bool read_debug_raw_data); //Sets number of sections - virtual void set_number_of_sections(uint16_t number) = 0; + void set_number_of_sections(uint16_t number); //Sets size of image - virtual void set_size_of_image(uint32_t number) = 0; - //Sets size of headers - virtual void set_size_of_headers(uint32_t size) = 0; - //Sets size of optional headers - virtual void set_size_of_optional_header(uint16_t size) = 0; - //Returns nt headers data pointer - virtual char* get_nt_headers_ptr() = 0; - //Returns sizeof() nt headers - virtual uint32_t get_sizeof_nt_header() const = 0; - //Returns sizeof() optional headers - virtual uint32_t get_sizeof_opt_headers() const = 0; + void set_size_of_image(uint32_t size); //Sets file alignment (no checks) - virtual void set_file_alignment_unchecked(uint32_t alignment) = 0; - //Sets base of code - virtual void set_base_of_code(uint32_t base) = 0; + void set_file_alignment_unchecked(uint32_t alignment); //Returns needed magic of image - virtual uint32_t get_needed_magic() const = 0; + uint32_t get_needed_magic() const; + //Returns nt headers data pointer + char* get_nt_headers_ptr(); -protected: +private: static const uint16_t maximum_number_of_sections = 0x60; static const uint32_t minimum_file_alignment = 512; - - //Helper function to recalculate RAW and virtual section sizes and strip it, if necessary - //auto_strip = strip section, if necessary - void recalculate_section_sizes(section& s, bool auto_strip); private: - //Returns array of exported functions and information about export (if info != 0) - const exported_functions_list get_exported_functions(export_info* info) const; - - //Processes resource directory - const resource_directory process_resource_directory(uint32_t res_rva, uint32_t offset_to_directory, std::set<uint32_t>& processed) const; - - //Section by file offset finder helper (4gb max) - struct section_by_raw_offset - { - public: - explicit section_by_raw_offset(uint32_t offset); - bool operator()(const section& s) const; - - private: - uint32_t offset_; - }; - //RAW file offset to section convertion helpers (4gb max) section_list::const_iterator file_offset_to_section(uint32_t offset) const; section_list::iterator file_offset_to_section(uint32_t offset); - - //Helper: finder of section* in sections list - struct section_ptr_finder - { - public: - explicit section_ptr_finder(const section& s); - bool operator()(const section& s) const; - - private: - const section& s_; - }; - - //Helper: sorts exported function list by ordinals - struct ordinal_sorter - { - public: - bool operator()(const exported_function& func1, const exported_function& func2) const; - }; - - //Helper: sorts resource directory entries - struct entry_sorter - { - public: - bool operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const; - }; - - //Helper function to calculate needed space for resource data - void calculate_resource_data_space(const resource_directory& root, uint32_t aligned_offset_from_section_start, uint32_t& needed_size_for_structures, uint32_t& needed_size_for_strings); - - //Helper function to calculate needed space for resource data - void calculate_resource_data_space(const resource_directory& root, uint32_t needed_size_for_structures, uint32_t needed_size_for_strings, uint32_t& needed_size_for_data, uint32_t& current_data_pos); - - //Helper function to rebuild resource directory - void rebuild_resource_directory(section& resource_section, resource_directory& root, uint32_t& current_structures_pos, uint32_t& current_data_pos, uint32_t& current_strings_pos, uint32_t offset_from_section_start); - - //Calculates entropy from bytes count - static double calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length); - -#ifndef PE_BLISS_WINDOWS -public: - static const u16string to_ucs2(const std::wstring& str); - static const std::wstring from_ucs2(const u16string& str); -#endif }; } diff --git a/pe_lib/pe_bliss.h b/pe_lib/pe_bliss.h new file mode 100644 index 0000000..00d62e9 --- /dev/null +++ b/pe_lib/pe_bliss.h @@ -0,0 +1,18 @@ +#pragma once +#include "pe_base.h" +#include "pe_rebuilder.h" +#include "pe_factory.h" +#include "pe_bound_import.h" +#include "pe_debug.h" +#include "pe_dotnet.h" +#include "pe_exception_directory.h" +#include "pe_exports.h" +#include "pe_imports.h" +#include "pe_load_config.h" +#include "pe_relocations.h" +#include "pe_resources.h" +#include "pe_rich_data.h" +#include "pe_tls.h" +#include "pe_properties_generic.h" +#include "pe_checksum.h" +#include "entropy.h" diff --git a/pe_lib/pe_bliss_resources.h b/pe_lib/pe_bliss_resources.h new file mode 100644 index 0000000..354752c --- /dev/null +++ b/pe_lib/pe_bliss_resources.h @@ -0,0 +1,15 @@ +#pragma once +#include "file_version_info.h" +#include "message_table.h" +#include "pe_resource_manager.h" +#include "pe_resource_viewer.h" +#include "version_info_editor.h" +#include "version_info_viewer.h" +#include "resource_bitmap_reader.h" +#include "resource_bitmap_writer.h" +#include "resource_cursor_icon_reader.h" +#include "resource_cursor_icon_writer.h" +#include "resource_version_info_reader.h" +#include "resource_version_info_writer.h" +#include "resource_string_table_reader.h" +#include "resource_message_list_reader.h" diff --git a/pe_lib/pe_bound_import.cpp b/pe_lib/pe_bound_import.cpp new file mode 100644 index 0000000..d8d1303 --- /dev/null +++ b/pe_lib/pe_bound_import.cpp @@ -0,0 +1,290 @@ +#include <string.h> +#include "pe_bound_import.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//BOUND IMPORT +//Default constructor +bound_import_ref::bound_import_ref() + :timestamp_(0) +{} + +//Constructor from data +bound_import_ref::bound_import_ref(const std::string& module_name, uint32_t timestamp) + :module_name_(module_name), timestamp_(timestamp) +{} + +//Returns imported module name +const std::string& bound_import_ref::get_module_name() const +{ + return module_name_; +} + +//Returns bound import date and time stamp +uint32_t bound_import_ref::get_timestamp() const +{ + return timestamp_; +} + +//Sets module name +void bound_import_ref::set_module_name(const std::string& module_name) +{ + module_name_ = module_name; +} + +//Sets timestamp +void bound_import_ref::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Default constructor +bound_import::bound_import() + :timestamp_(0) +{} + +//Constructor from data +bound_import::bound_import(const std::string& module_name, uint32_t timestamp) + :module_name_(module_name), timestamp_(timestamp) +{} + +//Returns imported module name +const std::string& bound_import::get_module_name() const +{ + return module_name_; +} + +//Returns bound import date and time stamp +uint32_t bound_import::get_timestamp() const +{ + return timestamp_; +} + +//Returns bound references cound +size_t bound_import::get_module_ref_count() const +{ + return refs_.size(); +} + +//Returns module references +const bound_import::ref_list& bound_import::get_module_ref_list() const +{ + return refs_; +} + +//Adds module reference +void bound_import::add_module_ref(const bound_import_ref& ref) +{ + refs_.push_back(ref); +} + +//Clears module references list +void bound_import::clear_module_refs() +{ + refs_.clear(); +} + +//Returns module references +bound_import::ref_list& bound_import::get_module_ref_list() +{ + return refs_; +} + +//Sets module name +void bound_import::set_module_name(const std::string& module_name) +{ + module_name_ = module_name; +} + +//Sets timestamp +void bound_import::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +const bound_import_module_list get_bound_import_module_list(const pe_base& pe) +{ + //Returned bound import modules list + bound_import_module_list ret; + + //If image has no bound imports + if(!pe.has_bound_import()) + return ret; + + uint32_t bound_import_data_len = + pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); + + if(bound_import_data_len < pe.get_directory_size(image_directory_entry_bound_import)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + const char* bound_import_data = pe.section_data_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); + + //Check read in "read_pe" function raw bound import data size + if(bound_import_data_len < sizeof(image_bound_import_descriptor)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //current bound_import_data_ in-string position + unsigned long current_pos = 0; + //first bound import descriptor + //so, we're working with raw data here, no section helpers available + const image_bound_import_descriptor* descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); + + //Enumerate until zero + while(descriptor->OffsetModuleName) + { + //Check module name offset + if(descriptor->OffsetModuleName >= bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Check module name for null-termination + if(!pe_utils::is_null_terminated(&bound_import_data[descriptor->OffsetModuleName], bound_import_data_len - descriptor->OffsetModuleName)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Create bound import descriptor structure + bound_import elem(&bound_import_data[descriptor->OffsetModuleName], descriptor->TimeDateStamp); + + //Check DWORDs + if(descriptor->NumberOfModuleForwarderRefs >= pe_utils::max_dword / sizeof(image_bound_forwarder_ref) + || !pe_utils::is_sum_safe(current_pos, 2 /* this descriptor and the next one */ * sizeof(image_bound_import_descriptor) + descriptor->NumberOfModuleForwarderRefs * sizeof(image_bound_forwarder_ref))) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Move after current descriptor + current_pos += sizeof(image_bound_import_descriptor); + + //Enumerate referenced bound import descriptors + for(unsigned long i = 0; i != descriptor->NumberOfModuleForwarderRefs; ++i) + { + //They're just after parent descriptor + //Check size of structure + if(current_pos + sizeof(image_bound_forwarder_ref) > bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Get IMAGE_BOUND_FORWARDER_REF pointer + const image_bound_forwarder_ref* ref_descriptor = reinterpret_cast<const image_bound_forwarder_ref*>(&bound_import_data[current_pos]); + + //Check referenced module name + if(ref_descriptor->OffsetModuleName >= bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //And its null-termination + if(!pe_utils::is_null_terminated(&bound_import_data[ref_descriptor->OffsetModuleName], bound_import_data_len - ref_descriptor->OffsetModuleName)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Add referenced module to current bound import structure + elem.add_module_ref(bound_import_ref(&bound_import_data[ref_descriptor->OffsetModuleName], ref_descriptor->TimeDateStamp)); + + //Move after referenced bound import descriptor + current_pos += sizeof(image_bound_forwarder_ref); + } + + //Check structure size + if(current_pos + sizeof(image_bound_import_descriptor) > bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Move to next bound import descriptor + descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); + + //Save created descriptor structure and references + ret.push_back(elem); + } + + //Return result + return ret; +} + +//imports - bound imported modules list +//imports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from imports_section raw data start +//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers +//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped +const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that exports_section is attached to this PE image + if(!pe.section_attached(imports_section)) + throw pe_exception("Bound import section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + uint32_t needed_size = sizeof(image_bound_import_descriptor) /* Ending null descriptor */; + uint32_t needed_size_for_strings = 0; + + //Calculate needed size for bound import data + for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + const bound_import& import = *it; + needed_size += sizeof(image_bound_import_descriptor); + needed_size_for_strings += static_cast<uint32_t>((*it).get_module_name().length()) + 1 /* nullbyte */; + + const bound_import::ref_list& refs = import.get_module_ref_list(); + for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) + { + needed_size_for_strings += static_cast<uint32_t>((*ref_it).get_module_name().length()) + 1 /* nullbyte */; + needed_size += sizeof(image_bound_forwarder_ref); + } + } + + needed_size += needed_size_for_strings; + + //Check if imports_section is last one. If it's not, check if there's enough place for bound import data + if(&imports_section != &*(pe.get_image_sections().end() - 1) && + (imports_section.empty() || pe_utils::align_up(imports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) + throw pe_exception("Insufficient space for bound import directory", pe_exception::insufficient_space); + + std::string& raw_data = imports_section.get_raw_data(); + + //This will be done only if imports_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + directory_pos) + raw_data.resize(needed_size + directory_pos); //Expand section raw data + + uint32_t current_pos_for_structures = directory_pos; + uint32_t current_pos_for_strings = current_pos_for_structures + needed_size - needed_size_for_strings; + + for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + const bound_import& import = *it; + image_bound_import_descriptor descriptor; + descriptor.NumberOfModuleForwarderRefs = static_cast<uint16_t>(import.get_module_ref_list().size()); + descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); + descriptor.TimeDateStamp = import.get_timestamp(); + + memcpy(&raw_data[current_pos_for_structures], &descriptor, sizeof(descriptor)); + current_pos_for_structures += sizeof(descriptor); + + size_t length = import.get_module_name().length() + 1 /* nullbyte */; + memcpy(&raw_data[current_pos_for_strings], import.get_module_name().c_str(), length); + current_pos_for_strings += static_cast<uint32_t>(length); + + const bound_import::ref_list& refs = import.get_module_ref_list(); + for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) + { + const bound_import_ref& ref = *ref_it; + image_bound_forwarder_ref ref_descriptor = {0}; + ref_descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); + ref_descriptor.TimeDateStamp = ref.get_timestamp(); + + memcpy(&raw_data[current_pos_for_structures], &ref_descriptor, sizeof(ref_descriptor)); + current_pos_for_structures += sizeof(ref_descriptor); + + length = ref.get_module_name().length() + 1 /* nullbyte */; + memcpy(&raw_data[current_pos_for_strings], ref.get_module_name().c_str(), length); + current_pos_for_strings += static_cast<uint32_t>(length); + } + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(imports_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(imports_section, directory_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_bound_import, ret.get_rva()); + pe.set_directory_size(image_directory_entry_bound_import, ret.get_size()); + } + + return ret; +} +} diff --git a/pe_lib/pe_bound_import.h b/pe_lib/pe_bound_import.h new file mode 100644 index 0000000..efa8b7d --- /dev/null +++ b/pe_lib/pe_bound_import.h @@ -0,0 +1,87 @@ +#pragma once +#include <vector> +#include <string> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing bound import reference +class bound_import_ref +{ +public: + //Default constructor + bound_import_ref(); + //Constructor from data + bound_import_ref(const std::string& module_name, uint32_t timestamp); + + //Returns imported module name + const std::string& get_module_name() const; + //Returns bound import date and time stamp + uint32_t get_timestamp() const; + +public: //Setters + //Sets module name + void set_module_name(const std::string& module_name); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + +private: + std::string module_name_; //Imported module name + uint32_t timestamp_; //Bound import timestamp +}; + +//Class representing image bound import information +class bound_import +{ +public: + typedef std::vector<bound_import_ref> ref_list; + +public: + //Default constructor + bound_import(); + //Constructor from data + bound_import(const std::string& module_name, uint32_t timestamp); + + //Returns imported module name + const std::string& get_module_name() const; + //Returns bound import date and time stamp + uint32_t get_timestamp() const; + + //Returns bound references cound + size_t get_module_ref_count() const; + //Returns module references + const ref_list& get_module_ref_list() const; + +public: //Setters + //Sets module name + void set_module_name(const std::string& module_name); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + + //Adds module reference + void add_module_ref(const bound_import_ref& ref); + //Clears module references list + void clear_module_refs(); + //Returns module references + ref_list& get_module_ref_list(); + +private: + std::string module_name_; //Imported module name + uint32_t timestamp_; //Bound import timestamp + ref_list refs_; //Module references list +}; + +typedef std::vector<bound_import> bound_import_module_list; + +//Returns bound import information +const bound_import_module_list get_bound_import_module_list(const pe_base& pe);//Export directory rebuilder + +//imports - bound imported modules list +//imports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from imports_section raw data start +//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers +//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped +const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/pe_lib/pe_checksum.cpp b/pe_lib/pe_checksum.cpp new file mode 100644 index 0000000..be74e8f --- /dev/null +++ b/pe_lib/pe_checksum.cpp @@ -0,0 +1,82 @@ +#include "pe_checksum.h" +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Calculate checksum of image +uint32_t calculate_checksum(std::istream& file) +{ + //Save istream state + std::ios_base::iostate state = file.exceptions(); + std::streamoff old_offset = file.tellg(); + + //Checksum value + unsigned long long checksum = 0; + + try + { + image_dos_header header; + + file.exceptions(std::ios::goodbit); + + //Read DOS header + pe_base::read_dos_header(file, header); + + //Calculate PE checksum + file.seekg(0); + unsigned long long top = 0xFFFFFFFF; + top++; + + //"CheckSum" field position in optional PE headers - it's always 64 for PE and PE+ + static const unsigned long checksum_pos_in_optional_headers = 64; + //Calculate real PE headers "CheckSum" field position + //Sum is safe here + unsigned long pe_checksum_pos = header.e_lfanew + sizeof(image_file_header) + sizeof(uint32_t) + checksum_pos_in_optional_headers; + + //Calculate checksum for each byte of file + std::streamoff filesize = pe_utils::get_file_size(file); + for(long long i = 0; i < filesize; i += 4) + { + unsigned long dw = 0; + + //Read DWORD from file + file.read(reinterpret_cast<char*>(&dw), sizeof(unsigned long)); + //Skip "CheckSum" DWORD + if(i == pe_checksum_pos) + continue; + + //Calculate checksum + checksum = (checksum & 0xffffffff) + dw + (checksum >> 32); + if(checksum > top) + checksum = (checksum & 0xffffffff) + (checksum >> 32); + } + + //Finish checksum + checksum = (checksum & 0xffff) + (checksum >> 16); + checksum = (checksum) + (checksum >> 16); + checksum = checksum & 0xffff; + + checksum += static_cast<unsigned long>(filesize); + } + catch(const std::exception&) + { + //If something went wrong, restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + //Rethrow + throw; + } + + //Restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + + //Return checksum + return static_cast<uint32_t>(checksum); +} +} diff --git a/pe_lib/pe_checksum.h b/pe_lib/pe_checksum.h new file mode 100644 index 0000000..7000ff7 --- /dev/null +++ b/pe_lib/pe_checksum.h @@ -0,0 +1,9 @@ +#pragma once +#include <istream> +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Calculate checksum of image (performs no checks on PE structures) +uint32_t calculate_checksum(std::istream& file); +} diff --git a/pe_lib/pe_debug.cpp b/pe_lib/pe_debug.cpp new file mode 100644 index 0000000..6fa0676 --- /dev/null +++ b/pe_lib/pe_debug.cpp @@ -0,0 +1,844 @@ +#include <string.h> +#include "pe_debug.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; +//DEBUG +//Default constructor +debug_info::debug_info() + :characteristics_(0), + time_stamp_(0), + major_version_(0), minor_version_(0), + type_(0), + size_of_data_(0), + address_of_raw_data_(0), + pointer_to_raw_data_(0), + advanced_info_type_(advanced_info_none) +{} + +//Constructor from data +debug_info::debug_info(const image_debug_directory& debug) + :characteristics_(debug.Characteristics), + time_stamp_(debug.TimeDateStamp), + major_version_(debug.MajorVersion), minor_version_(debug.MinorVersion), + type_(debug.Type), + size_of_data_(debug.SizeOfData), + address_of_raw_data_(debug.AddressOfRawData), + pointer_to_raw_data_(debug.PointerToRawData), + advanced_info_type_(advanced_info_none) +{} + +//Returns debug characteristics +uint32_t debug_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns debug datetimestamp +uint32_t debug_info::get_time_stamp() const +{ + return time_stamp_; +} + +//Returns major version +uint32_t debug_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version +uint32_t debug_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns type of debug info (unchecked) +uint32_t debug_info::get_type_raw() const +{ + return type_; +} + +//Returns type of debug info from debug_info_type enumeration +debug_info::debug_info_type debug_info::get_type() const +{ + //Determine debug type + switch(type_) + { + case image_debug_type_coff: + return debug_type_coff; + + case image_debug_type_codeview: + return debug_type_codeview; + + case image_debug_type_fpo: + return debug_type_fpo; + + case image_debug_type_misc: + return debug_type_misc; + + case image_debug_type_exception: + return debug_type_exception; + + case image_debug_type_fixup: + return debug_type_fixup; + + case image_debug_type_omap_to_src: + return debug_type_omap_to_src; + + case image_debug_type_omap_from_src: + return debug_type_omap_from_src; + + case image_debug_type_borland: + return debug_type_borland; + + case image_debug_type_clsid: + return debug_type_clsid; + + case image_debug_type_reserved10: + return debug_type_reserved10; + } + + return debug_type_unknown; +} + +//Returns size of debug data (internal, .pdb or other file doesn't count) +uint32_t debug_info::get_size_of_data() const +{ + return size_of_data_; +} + +//Returns RVA of debug info when mapped to memory or zero, if info is not mapped +uint32_t debug_info::get_rva_of_raw_data() const +{ + return address_of_raw_data_; +} + +//Returns raw file pointer to raw data +uint32_t debug_info::get_pointer_to_raw_data() const +{ + return pointer_to_raw_data_; +} + +//Copy constructor +debug_info::debug_info(const debug_info& info) + :characteristics_(info.characteristics_), + time_stamp_(info.time_stamp_), + major_version_(info.major_version_), minor_version_(info.minor_version_), + type_(info.type_), + size_of_data_(info.size_of_data_), + address_of_raw_data_(info.address_of_raw_data_), + pointer_to_raw_data_(info.pointer_to_raw_data_), + advanced_info_type_(info.advanced_info_type_) +{ + copy_advanced_info(info); +} + +//Copy assignment operator +debug_info& debug_info::operator=(const debug_info& info) +{ + copy_advanced_info(info); + + characteristics_ = info.characteristics_; + time_stamp_ = info.time_stamp_; + major_version_ = info.major_version_; + minor_version_ = info.minor_version_; + type_ = info.type_; + size_of_data_ = info.size_of_data_; + address_of_raw_data_ = info.address_of_raw_data_; + pointer_to_raw_data_ = info.pointer_to_raw_data_; + advanced_info_type_ = info.advanced_info_type_; + + return *this; +} + +//Default constructor +debug_info::advanced_info::advanced_info() + :adv_pdb_7_0_info(0) //Zero pointer to advanced data +{} + +//Returns true if advanced debug info is present +bool debug_info::advanced_info::is_present() const +{ + return adv_pdb_7_0_info != 0; +} + +//Helper for advanced debug information copying +void debug_info::copy_advanced_info(const debug_info& info) +{ + free_present_advanced_info(); + + switch(info.advanced_info_type_) + { + case advanced_info_pdb_7_0: + advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(*info.advanced_debug_info_.adv_pdb_7_0_info); + break; + case advanced_info_pdb_2_0: + advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(*info.advanced_debug_info_.adv_pdb_2_0_info); + break; + case advanced_info_misc: + advanced_debug_info_.adv_misc_info = new misc_debug_info(*info.advanced_debug_info_.adv_misc_info); + break; + case advanced_info_coff: + advanced_debug_info_.adv_coff_info = new coff_debug_info(*info.advanced_debug_info_.adv_coff_info); + break; + default: + break; + } + + advanced_info_type_ = info.advanced_info_type_; +} + +//Helper for clearing any present advanced debug information +void debug_info::free_present_advanced_info() +{ + switch(advanced_info_type_) + { + case advanced_info_pdb_7_0: + delete advanced_debug_info_.adv_pdb_7_0_info; + break; + case advanced_info_pdb_2_0: + delete advanced_debug_info_.adv_pdb_2_0_info; + break; + case advanced_info_misc: + delete advanced_debug_info_.adv_misc_info; + break; + case advanced_info_coff: + delete advanced_debug_info_.adv_coff_info; + break; + default: + break; + } + + advanced_debug_info_.adv_pdb_7_0_info = 0; + advanced_info_type_ = advanced_info_none; +} + +//Destructor +debug_info::~debug_info() +{ + free_present_advanced_info(); +} + +//Sets advanced debug information +void debug_info::set_advanced_debug_info(const pdb_7_0_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(info); + advanced_info_type_ = advanced_info_pdb_7_0; +} + +void debug_info::set_advanced_debug_info(const pdb_2_0_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(info); + advanced_info_type_ = advanced_info_pdb_2_0; +} + +void debug_info::set_advanced_debug_info(const misc_debug_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_misc_info = new misc_debug_info(info); + advanced_info_type_ = advanced_info_misc; +} + +void debug_info::set_advanced_debug_info(const coff_debug_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_coff_info = new coff_debug_info(info); + advanced_info_type_ = advanced_info_coff; +} + +//Returns advanced debug information type +debug_info::advanced_info_type debug_info::get_advanced_info_type() const +{ + return advanced_info_type_; +} + +//Returns advanced debug information or throws an exception, +//if requested information type is not contained by structure +template<> +const pdb_7_0_info debug_info::get_advanced_debug_info<pdb_7_0_info>() const +{ + if(advanced_info_type_ != advanced_info_pdb_7_0) + throw pe_exception("Debug info structure does not contain PDB 7.0 data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_pdb_7_0_info; +} + +template<> +const pdb_2_0_info debug_info::get_advanced_debug_info<pdb_2_0_info>() const +{ + if(advanced_info_type_ != advanced_info_pdb_2_0) + throw pe_exception("Debug info structure does not contain PDB 2.0 data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_pdb_2_0_info; +} + +template<> +const misc_debug_info debug_info::get_advanced_debug_info<misc_debug_info>() const +{ + if(advanced_info_type_ != advanced_info_misc) + throw pe_exception("Debug info structure does not contain MISC data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_misc_info; +} + +template<> +const coff_debug_info debug_info::get_advanced_debug_info<coff_debug_info>() const +{ + if(advanced_info_type_ != advanced_info_coff) + throw pe_exception("Debug info structure does not contain COFF data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_coff_info; +} + +//Sets advanced debug information type, if no advanced info structure available +void debug_info::set_advanced_info_type(advanced_info_type type) +{ + free_present_advanced_info(); + if(advanced_info_type_ >= advanced_info_codeview_4_0) //Don't set info type for those types, which have advanced info structures + advanced_info_type_ = type; +} + +//Default constructor +pdb_7_0_info::pdb_7_0_info() + :age_(0) +{ + memset(&guid_, 0, sizeof(guid_)); +} + +//Constructor from data +pdb_7_0_info::pdb_7_0_info(const CV_INFO_PDB70* info) + :age_(info->Age), guid_(info->Signature), + pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination +{} + +//Returns debug PDB 7.0 structure GUID +const guid pdb_7_0_info::get_guid() const +{ + return guid_; +} + +//Returns age of build +uint32_t pdb_7_0_info::get_age() const +{ + return age_; +} + +//Returns PDB file name / path +const std::string& pdb_7_0_info::get_pdb_file_name() const +{ + return pdb_file_name_; +} + +//Default constructor +pdb_2_0_info::pdb_2_0_info() + :age_(0), signature_(0) +{} + +//Constructor from data +pdb_2_0_info::pdb_2_0_info(const CV_INFO_PDB20* info) + :age_(info->Age), signature_(info->Signature), + pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination +{} + +//Returns debug PDB 2.0 structure signature +uint32_t pdb_2_0_info::get_signature() const +{ + return signature_; +} + +//Returns age of build +uint32_t pdb_2_0_info::get_age() const +{ + return age_; +} + +//Returns PDB file name / path +const std::string& pdb_2_0_info::get_pdb_file_name() const +{ + return pdb_file_name_; +} + +//Default constructor +misc_debug_info::misc_debug_info() + :data_type_(0), unicode_(false) +{} + +//Constructor from data +misc_debug_info::misc_debug_info(const image_debug_misc* info) + :data_type_(info->DataType), unicode_(info->Unicode ? true : false) +{ + //IMAGE_DEBUG_MISC::Data must be checked before! + if(info->Unicode) + { +#ifdef PE_BLISS_WINDOWS + debug_data_unicode_ = std::wstring(reinterpret_cast<const wchar_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2); +#else + debug_data_unicode_ = pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2)); +#endif + + pe_utils::strip_nullbytes(debug_data_unicode_); //Strip nullbytes in the end of string + } + else + { + debug_data_ansi_ = std::string(reinterpret_cast<const char*>(info->Data), info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */); + pe_utils::strip_nullbytes(debug_data_ansi_); //Strip nullbytes in the end of string + } +} + +//Returns debug data type +uint32_t misc_debug_info::get_data_type() const +{ + return data_type_; +} + +//Returns true if data type is exe name +bool misc_debug_info::is_exe_name() const +{ + return data_type_ == image_debug_misc_exename; +} + +//Returns true if debug data is UNICODE +bool misc_debug_info::is_unicode() const +{ + return unicode_; +} + +//Returns debug data (ANSI) +const std::string& misc_debug_info::get_data_ansi() const +{ + return debug_data_ansi_; +} + +//Returns debug data (UNICODE) +const std::wstring& misc_debug_info::get_data_unicode() const +{ + return debug_data_unicode_; +} + +//Default constructor +coff_debug_info::coff_debug_info() + :number_of_symbols_(0), + lva_to_first_symbol_(0), + number_of_line_numbers_(0), + lva_to_first_line_number_(0), + rva_to_first_byte_of_code_(0), + rva_to_last_byte_of_code_(0), + rva_to_first_byte_of_data_(0), + rva_to_last_byte_of_data_(0) +{} + +//Constructor from data +coff_debug_info::coff_debug_info(const image_coff_symbols_header* info) + :number_of_symbols_(info->NumberOfSymbols), + lva_to_first_symbol_(info->LvaToFirstSymbol), + number_of_line_numbers_(info->NumberOfLinenumbers), + lva_to_first_line_number_(info->LvaToFirstLinenumber), + rva_to_first_byte_of_code_(info->RvaToFirstByteOfCode), + rva_to_last_byte_of_code_(info->RvaToLastByteOfCode), + rva_to_first_byte_of_data_(info->RvaToFirstByteOfData), + rva_to_last_byte_of_data_(info->RvaToLastByteOfData) +{} + +//Returns number of symbols +uint32_t coff_debug_info::get_number_of_symbols() const +{ + return number_of_symbols_; +} + +//Returns virtual address of the first symbol +uint32_t coff_debug_info::get_lva_to_first_symbol() const +{ + return lva_to_first_symbol_; +} + +//Returns number of line-number entries +uint32_t coff_debug_info::get_number_of_line_numbers() const +{ + return number_of_line_numbers_; +} + +//Returns virtual address of the first line-number entry +uint32_t coff_debug_info::get_lva_to_first_line_number() const +{ + return lva_to_first_line_number_; +} + +//Returns relative virtual address of the first byte of code +uint32_t coff_debug_info::get_rva_to_first_byte_of_code() const +{ + return rva_to_first_byte_of_code_; +} + +//Returns relative virtual address of the last byte of code +uint32_t coff_debug_info::get_rva_to_last_byte_of_code() const +{ + return rva_to_last_byte_of_code_; +} + +//Returns relative virtual address of the first byte of data +uint32_t coff_debug_info::get_rva_to_first_byte_of_data() const +{ + return rva_to_first_byte_of_data_; +} + +//Returns relative virtual address of the last byte of data +uint32_t coff_debug_info::get_rva_to_last_byte_of_data() const +{ + return rva_to_last_byte_of_data_; +} + +//Returns COFF symbols list +const coff_debug_info::coff_symbols_list& coff_debug_info::get_symbols() const +{ + return symbols_; +} + +//Adds COFF symbol +void coff_debug_info::add_symbol(const coff_symbol& sym) +{ + symbols_.push_back(sym); +} + +//Default constructor +coff_debug_info::coff_symbol::coff_symbol() + :storage_class_(0), + index_(0), + section_number_(0), rva_(0), + type_(0), + is_filename_(false) +{} + +//Returns storage class +uint32_t coff_debug_info::coff_symbol::get_storage_class() const +{ + return storage_class_; +} + +//Returns symbol index +uint32_t coff_debug_info::coff_symbol::get_index() const +{ + return index_; +} + +//Returns section number +uint32_t coff_debug_info::coff_symbol::get_section_number() const +{ + return section_number_; +} + +//Returns RVA +uint32_t coff_debug_info::coff_symbol::get_rva() const +{ + return rva_; +} + +//Returns true if structure contains file name +bool coff_debug_info::coff_symbol::is_file() const +{ + return is_filename_; +} + +//Returns text data (symbol or file name) +const std::string& coff_debug_info::coff_symbol::get_symbol() const +{ + return name_; +} + +//Sets storage class +void coff_debug_info::coff_symbol::set_storage_class(uint32_t storage_class) +{ + storage_class_ = storage_class; +} + +//Sets symbol index +void coff_debug_info::coff_symbol::set_index(uint32_t index) +{ + index_ = index; +} + +//Sets section number +void coff_debug_info::coff_symbol::set_section_number(uint32_t section_number) +{ + section_number_ = section_number; +} + +//Sets RVA +void coff_debug_info::coff_symbol::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets file name +void coff_debug_info::coff_symbol::set_file_name(const std::string& file_name) +{ + name_ = file_name; + is_filename_ = true; +} + +//Sets symbol name +void coff_debug_info::coff_symbol::set_symbol_name(const std::string& symbol_name) +{ + name_ = symbol_name; + is_filename_ = false; +} + +//Returns type +uint16_t coff_debug_info::coff_symbol::get_type() const +{ + return type_; +} + +//Sets type +void coff_debug_info::coff_symbol::set_type(uint16_t type) +{ + type_ = type; +} + +//Returns debug information list +const debug_info_list get_debug_information(const pe_base& pe) +{ + debug_info_list ret; + + //If there's no debug directory, return empty list + if(!pe.has_debug()) + return ret; + + //Check the length in bytes of the section containing debug directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_rva(image_directory_entry_debug), section_data_virtual, true) + < sizeof(image_debug_directory)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_debug); + + //First IMAGE_DEBUG_DIRECTORY table + image_debug_directory directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + + if(!pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_size(image_directory_entry_debug))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Iterate over all IMAGE_DEBUG_DIRECTORY directories + while(directory.PointerToRawData + && current_pos < pe.get_directory_rva(image_directory_entry_debug) + pe.get_directory_size(image_directory_entry_debug)) + { + //Create debug information structure + debug_info info(directory); + + //Find raw debug data + const pe_base::debug_data_list& debug_datas = pe.get_raw_debug_data_list(); + pe_base::debug_data_list::const_iterator it = debug_datas.find(directory.PointerToRawData); + if(it != debug_datas.end()) //If it exists, we'll do some detailed debug info research + { + const std::string& debug_data = (*it).second; + switch(directory.Type) + { + case image_debug_type_coff: + { + //Check data length + if(debug_data.length() < sizeof(image_coff_symbols_header)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get coff header structure pointer + const image_coff_symbols_header* coff = reinterpret_cast<const image_coff_symbols_header*>(debug_data.data()); + + //Check possible overflows + if(coff->NumberOfSymbols >= pe_utils::max_dword / sizeof(image_symbol) + || !pe_utils::is_sum_safe(coff->NumberOfSymbols * sizeof(image_symbol), coff->LvaToFirstSymbol)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Check data length again + if(debug_data.length() < coff->NumberOfSymbols * sizeof(image_symbol) + coff->LvaToFirstSymbol) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Create COFF debug info structure + coff_debug_info coff_info(coff); + + //Enumerate debug symbols data + for(uint32_t i = 0; i < coff->NumberOfSymbols; ++i) + { + //Safe sum (checked above) + const image_symbol* sym = reinterpret_cast<const image_symbol*>(debug_data.data() + i * sizeof(image_symbol) + coff->LvaToFirstSymbol); + + coff_debug_info::coff_symbol symbol; + symbol.set_index(i); //Save symbol index + symbol.set_storage_class(sym->StorageClass); //Save storage class + symbol.set_type(sym->Type); //Save storage class + + //Check data length again + if(!pe_utils::is_sum_safe(i, sym->NumberOfAuxSymbols) + || (i + sym->NumberOfAuxSymbols) > coff->NumberOfSymbols + || debug_data.length() < (i + 1) * sizeof(image_symbol) + coff->LvaToFirstSymbol + sym->NumberOfAuxSymbols * sizeof(image_symbol)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //If symbol is filename + if(sym->StorageClass == image_sym_class_file) + { + //Save file name, it is situated just after this IMAGE_SYMBOL structure + std::string file_name(reinterpret_cast<const char*>(debug_data.data() + (i + 1) * sizeof(image_symbol)), sym->NumberOfAuxSymbols * sizeof(image_symbol)); + pe_utils::strip_nullbytes(file_name); + symbol.set_file_name(file_name); + + //Save symbol info + coff_info.add_symbol(symbol); + + //Move to next symbol + i += sym->NumberOfAuxSymbols; + continue; + } + + //Dump some other symbols + if(((sym->StorageClass == image_sym_class_static) + && (sym->NumberOfAuxSymbols == 0) + && (sym->SectionNumber == 1)) + || + ((sym->StorageClass == image_sym_class_external) + && ISFCN(sym->Type) + && (sym->SectionNumber > 0)) + ) + { + //Save RVA and section number + symbol.set_section_number(sym->SectionNumber); + symbol.set_rva(sym->Value); + + //If symbol has short name + if(sym->N.Name.Short) + { + //Copy and save symbol name + char name_buff[9]; + memcpy(name_buff, sym->N.ShortName, 8); + name_buff[8] = '\0'; + symbol.set_symbol_name(name_buff); + } + else + { + //Symbol has long name + + //Check possible overflows + if(!pe_utils::is_sum_safe(coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol), sym->N.Name.Long)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Here we have an offset to the string table + uint32_t symbol_offset = coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol) + sym->N.Name.Long; + + //Check data length + if(debug_data.length() < symbol_offset) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Check symbol name for null-termination + if(!pe_utils::is_null_terminated(debug_data.data() + symbol_offset, debug_data.length() - symbol_offset)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Save symbol name + symbol.set_symbol_name(debug_data.data() + symbol_offset); + } + + //Save symbol info + coff_info.add_symbol(symbol); + + //Move to next symbol + i += sym->NumberOfAuxSymbols; + continue; + } + } + + info.set_advanced_debug_info(coff_info); + } + break; + + case image_debug_type_codeview: + { + //Check data length + if(debug_data.length() < sizeof(OMFSignature*)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get POMFSignature structure pointer from the very beginning of debug data + const OMFSignature* sig = reinterpret_cast<const OMFSignature*>(debug_data.data()); + if(!memcmp(sig->Signature, "RSDS", 4)) + { + //Signature is "RSDS" - PDB 7.0 + + //Check data length + if(debug_data.length() < sizeof(CV_INFO_PDB70)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + const CV_INFO_PDB70* pdb_data = reinterpret_cast<const CV_INFO_PDB70*>(debug_data.data()); + + //Check PDB file name null-termination + if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB70) - 1 /* BYTE of filename in structure */))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + info.set_advanced_debug_info(pdb_7_0_info(pdb_data)); + } + else if(!memcmp(sig->Signature, "NB10", 4)) + { + //Signature is "NB10" - PDB 2.0 + + //Check data length + if(debug_data.length() < sizeof(CV_INFO_PDB20)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + const CV_INFO_PDB20* pdb_data = reinterpret_cast<const CV_INFO_PDB20*>(debug_data.data()); + + //Check PDB file name null-termination + if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB20) - 1 /* BYTE of filename in structure */))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + info.set_advanced_debug_info(pdb_2_0_info(pdb_data)); + } + else if(!memcmp(sig->Signature, "NB09", 4)) + { + //CodeView 4.0, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview_4_0); + } + else if(!memcmp(sig->Signature, "NB11", 4)) + { + //CodeView 5.0, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview_5_0); + } + else if(!memcmp(sig->Signature, "NB05", 4)) + { + //Other CodeView, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview); + } + } + + break; + + case image_debug_type_misc: + { + //Check data length + if(debug_data.length() < sizeof(image_debug_misc)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get misc structure pointer + const image_debug_misc* misc_data = reinterpret_cast<const image_debug_misc*>(debug_data.data()); + + //Check misc data length + if(debug_data.length() < misc_data->Length /* Total length of record */) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Save advanced information + info.set_advanced_debug_info(misc_debug_info(misc_data)); + } + break; + } + } + + //Save debug information structure + ret.push_back(info); + + //Check possible overflow + if(!pe_utils::is_sum_safe(current_pos, sizeof(image_debug_directory))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Go to next debug entry + current_pos += sizeof(image_debug_directory); + directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + } + + return ret; +} +} diff --git a/pe_lib/pe_debug.h b/pe_lib/pe_debug.h new file mode 100644 index 0000000..6341bff --- /dev/null +++ b/pe_lib/pe_debug.h @@ -0,0 +1,303 @@ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing advanced RSDS (PDB 7.0) information +class pdb_7_0_info +{ +public: + //Default constructor + pdb_7_0_info(); + //Constructor from data + explicit pdb_7_0_info(const pe_win::CV_INFO_PDB70* info); + + //Returns debug PDB 7.0 structure GUID + const pe_win::guid get_guid() const; + //Returns age of build + uint32_t get_age() const; + //Returns PDB file name / path + const std::string& get_pdb_file_name() const; + +private: + uint32_t age_; + pe_win::guid guid_; + std::string pdb_file_name_; +}; + +//Class representing advanced NB10 (PDB 2.0) information +class pdb_2_0_info +{ +public: + //Default constructor + pdb_2_0_info(); + //Constructor from data + explicit pdb_2_0_info(const pe_win::CV_INFO_PDB20* info); + + //Returns debug PDB 2.0 structure signature + uint32_t get_signature() const; + //Returns age of build + uint32_t get_age() const; + //Returns PDB file name / path + const std::string& get_pdb_file_name() const; + +private: + uint32_t age_; + uint32_t signature_; + std::string pdb_file_name_; +}; + +//Class representing advanced misc (IMAGE_DEBUG_TYPE_MISC) info +class misc_debug_info +{ +public: + //Default constructor + misc_debug_info(); + //Constructor from data + explicit misc_debug_info(const pe_win::image_debug_misc* info); + + //Returns debug data type + uint32_t get_data_type() const; + //Returns true if data type is exe name + bool is_exe_name() const; + + //Returns true if debug data is UNICODE + bool is_unicode() const; + //Returns debug data (ANSI or UNICODE) + const std::string& get_data_ansi() const; + const std::wstring& get_data_unicode() const; + +private: + uint32_t data_type_; + bool unicode_; + std::string debug_data_ansi_; + std::wstring debug_data_unicode_; +}; + +//Class representing COFF (IMAGE_DEBUG_TYPE_COFF) debug info +class coff_debug_info +{ +public: + //Structure representing COFF symbol + struct coff_symbol + { + public: + //Default constructor + coff_symbol(); + + //Returns storage class + uint32_t get_storage_class() const; + //Returns symbol index + uint32_t get_index() const; + //Returns section number + uint32_t get_section_number() const; + //Returns RVA + uint32_t get_rva() const; + //Returns type + uint16_t get_type() const; + + //Returns true if structure contains file name + bool is_file() const; + //Returns text data (symbol or file name) + const std::string& get_symbol() const; + + public: //These functions do not change everything inside image, they are used by PE class + //Sets storage class + void set_storage_class(uint32_t storage_class); + //Sets symbol index + void set_index(uint32_t index); + //Sets section number + void set_section_number(uint32_t section_number); + //Sets RVA + void set_rva(uint32_t rva); + //Sets type + void set_type(uint16_t type); + + //Sets file name + void set_file_name(const std::string& file_name); + //Sets symbol name + void set_symbol_name(const std::string& symbol_name); + + private: + uint32_t storage_class_; + uint32_t index_; + uint32_t section_number_, rva_; + uint16_t type_; + bool is_filename_; + std::string name_; + }; + +public: + typedef std::vector<coff_symbol> coff_symbols_list; + +public: + //Default constructor + coff_debug_info(); + //Constructor from data + explicit coff_debug_info(const pe_win::image_coff_symbols_header* info); + + //Returns number of symbols + uint32_t get_number_of_symbols() const; + //Returns virtual address of the first symbol + uint32_t get_lva_to_first_symbol() const; + //Returns number of line-number entries + uint32_t get_number_of_line_numbers() const; + //Returns virtual address of the first line-number entry + uint32_t get_lva_to_first_line_number() const; + //Returns relative virtual address of the first byte of code + uint32_t get_rva_to_first_byte_of_code() const; + //Returns relative virtual address of the last byte of code + uint32_t get_rva_to_last_byte_of_code() const; + //Returns relative virtual address of the first byte of data + uint32_t get_rva_to_first_byte_of_data() const; + //Returns relative virtual address of the last byte of data + uint32_t get_rva_to_last_byte_of_data() const; + + //Returns COFF symbols list + const coff_symbols_list& get_symbols() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Adds COFF symbol + void add_symbol(const coff_symbol& sym); + +private: + uint32_t number_of_symbols_; + uint32_t lva_to_first_symbol_; + uint32_t number_of_line_numbers_; + uint32_t lva_to_first_line_number_; + uint32_t rva_to_first_byte_of_code_; + uint32_t rva_to_last_byte_of_code_; + uint32_t rva_to_first_byte_of_data_; + uint32_t rva_to_last_byte_of_data_; + +private: + coff_symbols_list symbols_; +}; + +//Class representing debug information +class debug_info +{ +public: + //Enumeration of debug information types + enum debug_info_type + { + debug_type_unknown, + debug_type_coff, + debug_type_codeview, + debug_type_fpo, + debug_type_misc, + debug_type_exception, + debug_type_fixup, + debug_type_omap_to_src, + debug_type_omap_from_src, + debug_type_borland, + debug_type_reserved10, + debug_type_clsid + }; + +public: + //Enumeration of advanced debug information types + enum advanced_info_type + { + advanced_info_none, //No advanced info + advanced_info_pdb_7_0, //PDB 7.0 + advanced_info_pdb_2_0, //PDB 2.0 + advanced_info_misc, //MISC debug info + advanced_info_coff, //COFF debug info + //No advanced info structures available for types below + advanced_info_codeview_4_0, //CodeView 4.0 + advanced_info_codeview_5_0, //CodeView 5.0 + advanced_info_codeview //CodeView + }; + +public: + //Default constructor + debug_info(); + //Constructor from data + explicit debug_info(const pe_win::image_debug_directory& debug); + //Copy constructor + debug_info(const debug_info& info); + //Copy assignment operator + debug_info& operator=(const debug_info& info); + //Destructor + ~debug_info(); + + //Returns debug characteristics + uint32_t get_characteristics() const; + //Returns debug datetimestamp + uint32_t get_time_stamp() const; + //Returns major version + uint32_t get_major_version() const; + //Returns minor version + uint32_t get_minor_version() const; + //Returns type of debug info (unchecked) + uint32_t get_type_raw() const; + //Returns type of debug info from debug_info_type enumeration + debug_info_type get_type() const; + //Returns size of debug data (internal, .pdb or other file doesn't count) + uint32_t get_size_of_data() const; + //Returns RVA of debug info when mapped to memory or zero, if info is not mapped + uint32_t get_rva_of_raw_data() const; + //Returns raw file pointer to raw data + uint32_t get_pointer_to_raw_data() const; + + //Returns advanced debug information type + advanced_info_type get_advanced_info_type() const; + //Returns advanced debug information or throws an exception, + //if requested information type is not contained by structure + template<typename AdvancedInfo> + const AdvancedInfo get_advanced_debug_info() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Sets advanced debug information + void set_advanced_debug_info(const pdb_7_0_info& info); + void set_advanced_debug_info(const pdb_2_0_info& info); + void set_advanced_debug_info(const misc_debug_info& info); + void set_advanced_debug_info(const coff_debug_info& info); + + //Sets advanced debug information type, if no advanced info structure available + void set_advanced_info_type(advanced_info_type type); + +private: + uint32_t characteristics_; + uint32_t time_stamp_; + uint32_t major_version_, minor_version_; + uint32_t type_; + uint32_t size_of_data_; + uint32_t address_of_raw_data_; //RVA when mapped or 0 + uint32_t pointer_to_raw_data_; //RAW file offset + + //Union containing advanced debug information pointer + union advanced_info + { + public: + //Default constructor + advanced_info(); + + //Returns true if advanced debug info is present + bool is_present() const; + + public: + pdb_7_0_info* adv_pdb_7_0_info; + pdb_2_0_info* adv_pdb_2_0_info; + misc_debug_info* adv_misc_info; + coff_debug_info* adv_coff_info; + }; + + //Helper for advanced debug information copying + void copy_advanced_info(const debug_info& info); + //Helper for clearing any present advanced debug information + void free_present_advanced_info(); + + advanced_info advanced_debug_info_; + //Advanced information type + advanced_info_type advanced_info_type_; +}; + +typedef std::vector<debug_info> debug_info_list; + +//Returns debug information list +const debug_info_list get_debug_information(const pe_base& pe); +} diff --git a/pe_lib/pe_directory.cpp b/pe_lib/pe_directory.cpp new file mode 100644 index 0000000..0d5d09e --- /dev/null +++ b/pe_lib/pe_directory.cpp @@ -0,0 +1,38 @@ +#include "pe_directory.h" + +namespace pe_bliss +{ +//Default constructor +image_directory::image_directory() + :rva_(0), size_(0) +{} + +//Constructor from data +image_directory::image_directory(uint32_t rva, uint32_t size) + :rva_(rva), size_(size) +{} + +//Returns RVA +uint32_t image_directory::get_rva() const +{ + return rva_; +} + +//Returns size +uint32_t image_directory::get_size() const +{ + return size_; +} + +//Sets RVA +void image_directory::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets size +void image_directory::set_size(uint32_t size) +{ + size_ = size; +} +} diff --git a/pe_lib/pe_directory.h b/pe_lib/pe_directory.h new file mode 100644 index 0000000..4bd36c0 --- /dev/null +++ b/pe_lib/pe_directory.h @@ -0,0 +1,29 @@ +#pragma once +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Class representing image directory data +class image_directory +{ +public: + //Default constructor + image_directory(); + //Constructor from data + image_directory(uint32_t rva, uint32_t size); + + //Returns RVA + uint32_t get_rva() const; + //Returns size + uint32_t get_size() const; + + //Sets RVA + void set_rva(uint32_t rva); + //Sets size + void set_size(uint32_t size); + +private: + uint32_t rva_; + uint32_t size_; +}; +} diff --git a/pe_lib/pe_dotnet.cpp b/pe_lib/pe_dotnet.cpp new file mode 100644 index 0000000..2be5f59 --- /dev/null +++ b/pe_lib/pe_dotnet.cpp @@ -0,0 +1,165 @@ +#include <string.h> +#include "pe_dotnet.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//.NET +basic_dotnet_info::basic_dotnet_info() +{ + memset(&header_, 0, sizeof(header_)); +} + +//Constructor from data +basic_dotnet_info::basic_dotnet_info(const image_cor20_header& header) + :header_(header) +{} + +//Returns major runtime version +uint16_t basic_dotnet_info::get_major_runtime_version() const +{ + return header_.MajorRuntimeVersion; +} + +//Returns minor runtime version +uint16_t basic_dotnet_info::get_minor_runtime_version() const +{ + return header_.MinorRuntimeVersion; +} + +//Returns RVA of metadata (symbol table and startup information) +uint32_t basic_dotnet_info::get_rva_of_metadata() const +{ + return header_.MetaData.VirtualAddress; +} + +//Returns size of metadata (symbol table and startup information) +uint32_t basic_dotnet_info::get_size_of_metadata() const +{ + return header_.MetaData.Size; +} + +//Returns flags +uint32_t basic_dotnet_info::get_flags() const +{ + return header_.Flags; +} + +//Returns true if entry point is native +bool basic_dotnet_info::is_native_entry_point() const +{ + return (header_.Flags & comimage_flags_native_entrypoint) ? true : false; +} + +//Returns true if 32 bit required +bool basic_dotnet_info::is_32bit_required() const +{ + return (header_.Flags & comimage_flags_32bitrequired) ? true : false; +} + +//Returns true if image is IL library +bool basic_dotnet_info::is_il_library() const +{ + return (header_.Flags & comimage_flags_il_library) ? true : false; +} + +//Returns true if image uses IL only +bool basic_dotnet_info::is_il_only() const +{ + return (header_.Flags & comimage_flags_ilonly) ? true : false; +} + +//Returns entry point RVA (if entry point is native) +//Returns entry point managed token (if entry point is managed) +uint32_t basic_dotnet_info::get_entry_point_rva_or_token() const +{ + return header_.EntryPointToken; +} + +//Returns RVA of managed resources +uint32_t basic_dotnet_info::get_rva_of_resources() const +{ + return header_.Resources.VirtualAddress; +} + +//Returns size of managed resources +uint32_t basic_dotnet_info::get_size_of_resources() const +{ + return header_.Resources.Size; +} + +//Returns RVA of strong name signature +uint32_t basic_dotnet_info::get_rva_of_strong_name_signature() const +{ + return header_.StrongNameSignature.VirtualAddress; +} + +//Returns size of strong name signature +uint32_t basic_dotnet_info::get_size_of_strong_name_signature() const +{ + return header_.StrongNameSignature.Size; +} + +//Returns RVA of code manager table +uint32_t basic_dotnet_info::get_rva_of_code_manager_table() const +{ + return header_.CodeManagerTable.VirtualAddress; +} + +//Returns size of code manager table +uint32_t basic_dotnet_info::get_size_of_code_manager_table() const +{ + return header_.CodeManagerTable.Size; +} + +//Returns RVA of VTable fixups +uint32_t basic_dotnet_info::get_rva_of_vtable_fixups() const +{ + return header_.VTableFixups.VirtualAddress; +} + +//Returns size of VTable fixups +uint32_t basic_dotnet_info::get_size_of_vtable_fixups() const +{ + return header_.VTableFixups.Size; +} + +//Returns RVA of export address table jumps +uint32_t basic_dotnet_info::get_rva_of_export_address_table_jumps() const +{ + return header_.ExportAddressTableJumps.VirtualAddress; +} + +//Returns size of export address table jumps +uint32_t basic_dotnet_info::get_size_of_export_address_table_jumps() const +{ + return header_.ExportAddressTableJumps.Size; +} + +//Returns RVA of managed native header +//(precompiled header info, usually set to zero, for internal use) +uint32_t basic_dotnet_info::get_rva_of_managed_native_header() const +{ + return header_.ManagedNativeHeader.VirtualAddress; +} + +//Returns size of managed native header +//(precompiled header info, usually set to zero, for internal use) +uint32_t basic_dotnet_info::get_size_of_managed_native_header() const +{ + return header_.ManagedNativeHeader.Size; +} + +//Returns basic .NET information +//If image is not native, throws an exception +const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe) +{ + //If there's no debug directory, return empty list + if(!pe.is_dotnet()) + throw pe_exception("Image does not have managed code", pe_exception::image_does_not_have_managed_code); + + //Return basic .NET information + return basic_dotnet_info(pe.section_data_from_rva<image_cor20_header>(pe.get_directory_rva(image_directory_entry_com_descriptor), section_data_virtual, true)); +} +} diff --git a/pe_lib/pe_dotnet.h b/pe_lib/pe_dotnet.h new file mode 100644 index 0000000..0174644 --- /dev/null +++ b/pe_lib/pe_dotnet.h @@ -0,0 +1,76 @@ +#pragma once +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing basic .NET header information +class basic_dotnet_info +{ +public: + //Default constructor + basic_dotnet_info(); + //Constructor from data + explicit basic_dotnet_info(const pe_win::image_cor20_header& header); + + //Returns major runtime version + uint16_t get_major_runtime_version() const; + //Returns minor runtime version + uint16_t get_minor_runtime_version() const; + + //Returns RVA of metadata (symbol table and startup information) + uint32_t get_rva_of_metadata() const; + //Returns size of metadata (symbol table and startup information) + uint32_t get_size_of_metadata() const; + + //Returns flags + uint32_t get_flags() const; + + //Returns true if entry point is native + bool is_native_entry_point() const; + //Returns true if 32 bit required + bool is_32bit_required() const; + //Returns true if image is IL library + bool is_il_library() const; + //Returns true if image uses IL only + bool is_il_only() const; + + //Returns entry point RVA (if entry point is native) + //Returns entry point managed token (if entry point is managed) + uint32_t get_entry_point_rva_or_token() const; + + //Returns RVA of managed resources + uint32_t get_rva_of_resources() const; + //Returns size of managed resources + uint32_t get_size_of_resources() const; + //Returns RVA of strong name signature + uint32_t get_rva_of_strong_name_signature() const; + //Returns size of strong name signature + uint32_t get_size_of_strong_name_signature() const; + //Returns RVA of code manager table + uint32_t get_rva_of_code_manager_table() const; + //Returns size of code manager table + uint32_t get_size_of_code_manager_table() const; + //Returns RVA of VTable fixups + uint32_t get_rva_of_vtable_fixups() const; + //Returns size of VTable fixups + uint32_t get_size_of_vtable_fixups() const; + //Returns RVA of export address table jumps + uint32_t get_rva_of_export_address_table_jumps() const; + //Returns size of export address table jumps + uint32_t get_size_of_export_address_table_jumps() const; + //Returns RVA of managed native header + //(precompiled header info, usually set to zero, for internal use) + uint32_t get_rva_of_managed_native_header() const; + //Returns size of managed native header + //(precompiled header info, usually set to zero, for internal use) + uint32_t get_size_of_managed_native_header() const; + +private: + pe_win::image_cor20_header header_; +}; + +//Returns basic .NET information +//If image is not native, throws an exception +const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe); +} diff --git a/pe_lib/pe_exception.h b/pe_lib/pe_exception.h index 27172f2..8fe673f 100644 --- a/pe_lib/pe_exception.h +++ b/pe_lib/pe_exception.h @@ -76,13 +76,19 @@ public: exports_list_is_empty, duplicate_exported_function_ordinal, duplicate_exported_function_name, - duplicate_exported_function_name_ordinal, version_info_string_does_not_exist, no_more_sections_can_be_added, - encoding_convertion_error + no_icon_group_found, + no_cursor_group_found, + + encoding_convertion_error, + + error_expanding_section, + + cannot_rebuild_image }; public: diff --git a/pe_lib/pe_exception_directory.cpp b/pe_lib/pe_exception_directory.cpp new file mode 100644 index 0000000..6a49529 --- /dev/null +++ b/pe_lib/pe_exception_directory.cpp @@ -0,0 +1,156 @@ +#include "pe_exception_directory.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//EXCEPTION DIRECTORY (exists on PE+ only) +//Default constructor +exception_entry::exception_entry() + :begin_address_(0), end_address_(0), unwind_info_address_(0), + unwind_info_version_(0), + flags_(0), + size_of_prolog_(0), + count_of_codes_(0), + frame_register_(0), + frame_offset_(0) +{} + +//Constructor from data +exception_entry::exception_entry(const image_runtime_function_entry& entry, const unwind_info& unwind_info) + :begin_address_(entry.BeginAddress), end_address_(entry.EndAddress), unwind_info_address_(entry.UnwindInfoAddress), + unwind_info_version_(unwind_info.Version), + flags_(unwind_info.Flags), + size_of_prolog_(unwind_info.SizeOfProlog), + count_of_codes_(unwind_info.CountOfCodes), + frame_register_(unwind_info.FrameRegister), + frame_offset_(unwind_info.FrameOffset) +{} + +//Returns starting address of function, affected by exception unwinding +uint32_t exception_entry::get_begin_address() const +{ + return begin_address_; +} + +//Returns ending address of function, affected by exception unwinding +uint32_t exception_entry::get_end_address() const +{ + return end_address_; +} + +//Returns unwind info address +uint32_t exception_entry::get_unwind_info_address() const +{ + return unwind_info_address_; +} + +//Returns UNWIND_INFO structure version +uint8_t exception_entry::get_unwind_info_version() const +{ + return unwind_info_version_; +} + +//Returns unwind info flags +uint8_t exception_entry::get_flags() const +{ + return flags_; +} + +//The function has an exception handler that should be called +//when looking for functions that need to examine exceptions +bool exception_entry::has_exception_handler() const +{ + return (flags_ & unw_flag_ehandler) ? true : false; +} + +//The function has a termination handler that should be called +//when unwinding an exception +bool exception_entry::has_termination_handler() const +{ + return (flags_ & unw_flag_uhandler) ? true : false; +} + +//The unwind info structure is not the primary one for the procedure +bool exception_entry::is_chaininfo() const +{ + return (flags_ & unw_flag_chaininfo) ? true : false; +} + +//Returns size of function prolog +uint8_t exception_entry::get_size_of_prolog() const +{ + return size_of_prolog_; +} + +//Returns number of unwind slots +uint8_t exception_entry::get_number_of_unwind_slots() const +{ + return count_of_codes_; +} + +//If the function uses frame pointer +bool exception_entry::uses_frame_pointer() const +{ + return frame_register_ != 0; +} + +//Number of the nonvolatile register used as the frame pointer, +//using the same encoding for the operation info field of UNWIND_CODE nodes +uint8_t exception_entry::get_frame_pointer_register_number() const +{ + return frame_register_; +} + +//The scaled offset from RSP that is applied to the FP reg when it is established. +//The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 +uint8_t exception_entry::get_scaled_rsp_offset() const +{ + return frame_offset_; +} + +//Returns exception directory data (exists on PE+ only) +//Unwind opcodes are not listed, because their format and list are subject to change +const exception_entry_list get_exception_directory_data(const pe_base& pe) +{ + exception_entry_list ret; + + //If image doesn't have exception directory, return empty list + if(!pe.has_exception_directory()) + return ret; + + //Check the length in bytes of the section containing exception directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_exception), pe.get_directory_rva(image_directory_entry_exception), section_data_virtual, true) + < sizeof(image_runtime_function_entry)) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_exception); + + //Check if structures are DWORD-aligned + if(current_pos % sizeof(uint32_t)) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + //First IMAGE_RUNTIME_FUNCTION_ENTRY table + image_runtime_function_entry exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); + + //todo: virtual addresses BeginAddress and EndAddress are not checked to be inside image + while(exception_table.BeginAddress) + { + //Check addresses + if(exception_table.BeginAddress > exception_table.EndAddress) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + //Get unwind information + unwind_info info = pe.section_data_from_rva<unwind_info>(exception_table.UnwindInfoAddress, section_data_virtual, true); + + //Create exception entry and save it + ret.push_back(exception_entry(exception_table, info)); + + //Go to next exception entry + current_pos += sizeof(image_runtime_function_entry); + exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); + } + + return ret; +} +} diff --git a/pe_lib/pe_exception_directory.h b/pe_lib/pe_exception_directory.h new file mode 100644 index 0000000..d081efa --- /dev/null +++ b/pe_lib/pe_exception_directory.h @@ -0,0 +1,67 @@ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing exception directory entry +class exception_entry +{ +public: + //Default constructor + exception_entry(); + //Constructor from data + exception_entry(const pe_win::image_runtime_function_entry& entry, const pe_win::unwind_info& unwind_info); + + //Returns starting address of function, affected by exception unwinding + uint32_t get_begin_address() const; + //Returns ending address of function, affected by exception unwinding + uint32_t get_end_address() const; + //Returns unwind info address + uint32_t get_unwind_info_address() const; + + //Returns UNWIND_INFO structure version + uint8_t get_unwind_info_version() const; + + //Returns unwind info flags + uint8_t get_flags() const; + //The function has an exception handler that should be called + //when looking for functions that need to examine exceptions + bool has_exception_handler() const; + //The function has a termination handler that should be called + //when unwinding an exception + bool has_termination_handler() const; + //The unwind info structure is not the primary one for the procedure + bool is_chaininfo() const; + + //Returns size of function prolog + uint8_t get_size_of_prolog() const; + + //Returns number of unwind slots + uint8_t get_number_of_unwind_slots() const; + + //If the function uses frame pointer + bool uses_frame_pointer() const; + //Number of the nonvolatile register used as the frame pointer, + //using the same encoding for the operation info field of UNWIND_CODE nodes + uint8_t get_frame_pointer_register_number() const; + //The scaled offset from RSP that is applied to the FP reg when it is established. + //The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 + uint8_t get_scaled_rsp_offset() const; + +private: + uint32_t begin_address_, end_address_, unwind_info_address_; + uint8_t unwind_info_version_; + uint8_t flags_; + uint8_t size_of_prolog_; + uint8_t count_of_codes_; + uint8_t frame_register_, frame_offset_; +}; + +typedef std::vector<exception_entry> exception_entry_list; + +//Returns exception directory data (exists on PE+ only) +//Unwind opcodes are not listed, because their format and list are subject to change +const exception_entry_list get_exception_directory_data(const pe_base& pe); +} diff --git a/pe_lib/pe_exports.cpp b/pe_lib/pe_exports.cpp new file mode 100644 index 0000000..54dd378 --- /dev/null +++ b/pe_lib/pe_exports.cpp @@ -0,0 +1,679 @@ +#include <set> +#include <algorithm> +#include <string.h> +#include "pe_exports.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//EXPORTS +//Default constructor +exported_function::exported_function() + :ordinal_(0), rva_(0), has_name_(false), name_ordinal_(0), forward_(false) +{} + +//Returns ordinal of function (actually, ordinal = hint + ordinal base) +uint16_t exported_function::get_ordinal() const +{ + return ordinal_; +} + +//Returns RVA of function +uint32_t exported_function::get_rva() const +{ + return rva_; +} + +//Returns name of function +const std::string& exported_function::get_name() const +{ + return name_; +} + +//Returns true if function has name and name ordinal +bool exported_function::has_name() const +{ + return has_name_; +} + +//Returns name ordinal of function +uint16_t exported_function::get_name_ordinal() const +{ + return name_ordinal_; +} + +//Returns true if function is forwarded to other library +bool exported_function::is_forwarded() const +{ + return forward_; +} + +//Returns the name of forwarded function +const std::string& exported_function::get_forwarded_name() const +{ + return forward_name_; +} + +//Sets ordinal of function +void exported_function::set_ordinal(uint16_t ordinal) +{ + ordinal_ = ordinal; +} + +//Sets RVA of function +void exported_function::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets name of function (or clears it, if empty name is passed) +void exported_function::set_name(const std::string& name) +{ + name_ = name; + has_name_ = !name.empty(); +} + +//Sets name ordinal +void exported_function::set_name_ordinal(uint16_t name_ordinal) +{ + name_ordinal_ = name_ordinal; +} + +//Sets forwarded function name (or clears it, if empty name is passed) +void exported_function::set_forwarded_name(const std::string& name) +{ + forward_name_ = name; + forward_ = !name.empty(); +} + +//Default constructor +export_info::export_info() + :characteristics_(0), + timestamp_(0), + major_version_(0), + minor_version_(0), + ordinal_base_(0), + number_of_functions_(0), + number_of_names_(0), + address_of_functions_(0), + address_of_names_(0), + address_of_name_ordinals_(0) +{} + +//Returns characteristics +uint32_t export_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns timestamp +uint32_t export_info::get_timestamp() const +{ + return timestamp_; +} + +//Returns major version +uint16_t export_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version +uint16_t export_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns DLL name +const std::string& export_info::get_name() const +{ + return name_; +} + +//Returns ordinal base +uint32_t export_info::get_ordinal_base() const +{ + return ordinal_base_; +} + +//Returns number of functions +uint32_t export_info::get_number_of_functions() const +{ + return number_of_functions_; +} + +//Returns number of function names +uint32_t export_info::get_number_of_names() const +{ + return number_of_names_; +} + +//Returns RVA of function address table +uint32_t export_info::get_rva_of_functions() const +{ + return address_of_functions_; +} + +//Returns RVA of function name address table +uint32_t export_info::get_rva_of_names() const +{ + return address_of_names_; +} + +//Returns RVA of name ordinals table +uint32_t export_info::get_rva_of_name_ordinals() const +{ + return address_of_name_ordinals_; +} + +//Sets characteristics +void export_info::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets timestamp +void export_info::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Sets major version +void export_info::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version +void export_info::set_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Sets DLL name +void export_info::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets ordinal base +void export_info::set_ordinal_base(uint32_t ordinal_base) +{ + ordinal_base_ = ordinal_base; +} + +//Sets number of functions +void export_info::set_number_of_functions(uint32_t number_of_functions) +{ + number_of_functions_ = number_of_functions; +} + +//Sets number of function names +void export_info::set_number_of_names(uint32_t number_of_names) +{ + number_of_names_ = number_of_names; +} + +//Sets RVA of function address table +void export_info::set_rva_of_functions(uint32_t rva_of_functions) +{ + address_of_functions_ = rva_of_functions; +} + +//Sets RVA of function name address table +void export_info::set_rva_of_names(uint32_t rva_of_names) +{ + address_of_names_ = rva_of_names; +} + +//Sets RVA of name ordinals table +void export_info::set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals) +{ + address_of_name_ordinals_ = rva_of_name_ordinals; +} + +const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info); + +//Returns array of exported functions +const exported_functions_list get_exported_functions(const pe_base& pe) +{ + return get_exported_functions(pe, 0); +} + +//Returns array of exported functions and information about export +const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info) +{ + return get_exported_functions(pe, &info); +} + +//Helper: sorts exported function list by ordinals +struct ordinal_sorter +{ +public: + bool operator()(const exported_function& func1, const exported_function& func2) const; +}; + +//Returns array of exported functions and information about export (if info != 0) +const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info) +{ + //Returned exported functions info array + std::vector<exported_function> ret; + + if(pe.has_exports()) + { + //Check the length in bytes of the section containing export directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_export), + pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true) + < sizeof(image_export_directory)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + image_export_directory exports = pe.section_data_from_rva<image_export_directory>(pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true); + + unsigned long max_name_length; + + if(info) + { + //Save some export info data + info->set_characteristics(exports.Characteristics); + info->set_major_version(exports.MajorVersion); + info->set_minor_version(exports.MinorVersion); + + //Get byte count that we have for dll name + if((max_name_length = pe.section_data_length_from_rva(exports.Name, exports.Name, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get dll name pointer + const char* dll_name = pe.section_data_from_rva(exports.Name, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(dll_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Save the rest of export information data + info->set_name(dll_name); + info->set_number_of_functions(exports.NumberOfFunctions); + info->set_number_of_names(exports.NumberOfNames); + info->set_ordinal_base(exports.Base); + info->set_rva_of_functions(exports.AddressOfFunctions); + info->set_rva_of_names(exports.AddressOfNames); + info->set_rva_of_name_ordinals(exports.AddressOfNameOrdinals); + info->set_timestamp(exports.TimeDateStamp); + } + + if(!exports.NumberOfFunctions) + return ret; + + //Check IMAGE_EXPORT_DIRECTORY fields + if(exports.NumberOfNames > exports.NumberOfFunctions) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Check some export directory fields + if((!exports.AddressOfNameOrdinals && exports.AddressOfNames) || + (exports.AddressOfNameOrdinals && !exports.AddressOfNames) || + !exports.AddressOfFunctions + || exports.NumberOfFunctions >= pe_utils::max_dword / sizeof(uint32_t) + || exports.NumberOfNames > pe_utils::max_dword / sizeof(uint32_t) + || !pe_utils::is_sum_safe(exports.AddressOfFunctions, exports.NumberOfFunctions * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(exports.AddressOfNames, exports.NumberOfNames * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(exports.AddressOfNameOrdinals, exports.NumberOfFunctions * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_export), pe.get_directory_size(image_directory_entry_export))) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Check if it is enough bytes to hold AddressOfFunctions table + if(pe.section_data_length_from_rva(exports.AddressOfFunctions, exports.AddressOfFunctions, section_data_virtual, true) + < exports.NumberOfFunctions * sizeof(uint32_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + if(exports.AddressOfNames) + { + //Check if it is enough bytes to hold name and ordinal tables + if(pe.section_data_length_from_rva(exports.AddressOfNameOrdinals, exports.AddressOfNameOrdinals, section_data_virtual, true) + < exports.NumberOfNames * sizeof(uint16_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + if(pe.section_data_length_from_rva(exports.AddressOfNames, exports.AddressOfNames, section_data_virtual, true) + < exports.NumberOfNames * sizeof(uint32_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + } + + for(uint32_t ordinal = 0; ordinal < exports.NumberOfFunctions; ordinal++) + { + //Get function address + //Sum and multiplication are safe (checked above) + uint32_t rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfFunctions + ordinal * sizeof(uint32_t), section_data_virtual, true); + + //If we have a skip + if(!rva) + continue; + + exported_function func; + func.set_rva(rva); + + if(!pe_utils::is_sum_safe(exports.Base, ordinal) || exports.Base + ordinal > pe_utils::max_word) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + func.set_ordinal(static_cast<uint16_t>(ordinal + exports.Base)); + + //Scan for function name ordinal + for(uint32_t i = 0; i < exports.NumberOfNames; i++) + { + uint16_t ordinal2 = pe.section_data_from_rva<uint16_t>(exports.AddressOfNameOrdinals + i * sizeof(uint16_t), section_data_virtual, true); + + //If function has name (and name ordinal) + if(ordinal == ordinal2) + { + //Get function name + //Sum and multiplication are safe (checked above) + uint32_t function_name_rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfNames + i * sizeof(uint32_t), section_data_virtual, true); + + //Get byte count that we have for function name + if((max_name_length = pe.section_data_length_from_rva(function_name_rva, function_name_rva, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get function name pointer + const char* func_name = pe.section_data_from_rva(function_name_rva, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(func_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Save function info + func.set_name(func_name); + func.set_name_ordinal(ordinal2); + + //If the function is just a redirect, save its name + if(rva >= pe.get_directory_rva(image_directory_entry_export) + sizeof(image_directory_entry_export) && + rva < pe.get_directory_rva(image_directory_entry_export) + pe.get_directory_size(image_directory_entry_export)) + { + if((max_name_length = pe.section_data_length_from_rva(rva, rva, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get forwarded function name pointer + const char* forwarded_func_name = pe.section_data_from_rva(rva, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(forwarded_func_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Set the name of forwarded function + func.set_forwarded_name(forwarded_func_name); + } + + break; + } + } + + //Add function info to output array + ret.push_back(func); + } + } + + return ret; +} + +//Helper export functions +//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> +const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports) +{ + if(exports.empty()) + return std::make_pair(0, 0); + + uint16_t max_ordinal = 0; //Maximum ordinal number + uint16_t ordinal_base = pe_utils::max_word; //Minimum ordinal value + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + + //Calculate maximum and minimum ordinal numbers + max_ordinal = std::max<uint16_t>(max_ordinal, func.get_ordinal()); + ordinal_base = std::min<uint16_t>(ordinal_base, func.get_ordinal()); + } + + return std::make_pair(ordinal_base, max_ordinal); +} + +//Checks if exported function name already exists +bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports) +{ + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + if((*it).has_name() && (*it).get_name() == function_name) + return true; + } + + return false; +} + +//Checks if exported function name already exists +bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports) +{ + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + if((*it).get_ordinal() == ordinal) + return true; + } + + return false; +} + +//Helper: sorts exported function list by ordinals +bool ordinal_sorter::operator()(const exported_function& func1, const exported_function& func2) const +{ + return func1.get_ordinal() < func2.get_ordinal(); +} + +//Export directory rebuilder +//info - export information +//exported_functions_list - list of exported functions +//exports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from exports_section raw data start +//save_to_pe_headers - if true, new export directory information will be saved to PE image headers +//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped +//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently +//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure +//Returns new export directory information +//exported_functions_list is copied intentionally to be sorted by ordinal values later +//Name ordinals in exported function don't matter, they will be recalculated +const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that exports_section is attached to this PE image + if(!pe.section_attached(exports_section)) + throw pe_exception("Exports section must be attached to PE file", pe_exception::section_is_not_attached); + + //Needed space for strings + uint32_t needed_size_for_strings = static_cast<uint32_t>(info.get_name().length() + 1); + uint32_t number_of_names = 0; //Number of named functions + uint32_t max_ordinal = 0; //Maximum ordinal number + uint32_t ordinal_base = static_cast<uint32_t>(-1); //Minimum ordinal value + + if(exports.empty()) + ordinal_base = info.get_ordinal_base(); + + uint32_t needed_size_for_function_names = 0; //Needed space for function name strings + uint32_t needed_size_for_function_forwards = 0; //Needed space for function forwards names + + //List all exported functions + //Calculate needed size for function list + { + //Also check that there're no duplicate names and ordinals + std::set<std::string> used_function_names; + std::set<uint16_t> used_function_ordinals; + + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + //Calculate maximum and minimum ordinal numbers + max_ordinal = std::max<uint32_t>(max_ordinal, func.get_ordinal()); + ordinal_base = std::min<uint32_t>(ordinal_base, func.get_ordinal()); + + //Check if ordinal is unique + if(!used_function_ordinals.insert(func.get_ordinal()).second) + throw pe_exception("Duplicate exported function ordinal", pe_exception::duplicate_exported_function_ordinal); + + if(func.has_name()) + { + //If function is named + ++number_of_names; + needed_size_for_function_names += static_cast<uint32_t>(func.get_name().length() + 1); + + //Check if it's name and name ordinal are unique + if(!used_function_names.insert(func.get_name()).second) + throw pe_exception("Duplicate exported function name", pe_exception::duplicate_exported_function_name); + } + + //If function is forwarded to another DLL + if(func.is_forwarded()) + needed_size_for_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); + } + } + + //Sort functions by ordinal value + std::sort(exports.begin(), exports.end(), ordinal_sorter()); + + //Calculate needed space for different things... + needed_size_for_strings += needed_size_for_function_names; + needed_size_for_strings += needed_size_for_function_forwards; + uint32_t needed_size_for_function_name_ordinals = number_of_names * sizeof(uint16_t); + uint32_t needed_size_for_function_name_rvas = number_of_names * sizeof(uint32_t); + uint32_t needed_size_for_function_addresses = (max_ordinal - ordinal_base + 1) * sizeof(uint32_t); + + //Export directory header will be placed first + uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + + uint32_t needed_size = sizeof(image_export_directory); //Calculate needed size for export tables and strings + //sizeof(IMAGE_EXPORT_DIRECTORY) = export directory header + + //Total needed space... + needed_size += needed_size_for_function_name_ordinals; //For list of names ordinals + needed_size += needed_size_for_function_addresses; //For function RVAs + needed_size += needed_size_for_strings; //For all strings + needed_size += needed_size_for_function_name_rvas; //For function name strings RVAs + + //Check if exports_section is last one. If it's not, check if there's enough place for exports data + if(&exports_section != &*(pe.get_image_sections().end() - 1) && + (exports_section.empty() || pe_utils::align_up(exports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) + throw pe_exception("Insufficient space for export directory", pe_exception::insufficient_space); + + std::string& raw_data = exports_section.get_raw_data(); + + //This will be done only if exports_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + directory_pos) + raw_data.resize(needed_size + directory_pos); //Expand section raw data + + //Library name will be placed after it + uint32_t current_pos_of_function_names = static_cast<uint32_t>(info.get_name().length() + 1 + directory_pos + sizeof(image_export_directory)); + //Next - function names + uint32_t current_pos_of_function_name_ordinals = current_pos_of_function_names + needed_size_for_function_names; + //Next - function name ordinals + uint32_t current_pos_of_function_forwards = current_pos_of_function_name_ordinals + needed_size_for_function_name_ordinals; + //Finally - function addresses + uint32_t current_pos_of_function_addresses = current_pos_of_function_forwards + needed_size_for_function_forwards; + //Next - function names RVAs + uint32_t current_pos_of_function_names_rvas = current_pos_of_function_addresses + needed_size_for_function_addresses; + + { + //Create export directory and fill it + image_export_directory dir = {0}; + dir.Characteristics = info.get_characteristics(); + dir.MajorVersion = info.get_major_version(); + dir.MinorVersion = info.get_minor_version(); + dir.TimeDateStamp = info.get_timestamp(); + dir.NumberOfFunctions = max_ordinal - ordinal_base + 1; + dir.NumberOfNames = number_of_names; + dir.Base = ordinal_base; + dir.AddressOfFunctions = pe.rva_from_section_offset(exports_section, current_pos_of_function_addresses); + dir.AddressOfNameOrdinals = pe.rva_from_section_offset(exports_section, current_pos_of_function_name_ordinals); + dir.AddressOfNames = pe.rva_from_section_offset(exports_section, current_pos_of_function_names_rvas); + dir.Name = pe.rva_from_section_offset(exports_section, directory_pos + sizeof(image_export_directory)); + + //Save it + memcpy(&raw_data[directory_pos], &dir, sizeof(dir)); + } + + //Sve library name + memcpy(&raw_data[directory_pos + sizeof(image_export_directory)], info.get_name().c_str(), info.get_name().length() + 1); + + //A map to sort function names alphabetically + typedef std::map<std::string, uint16_t> funclist; //function name; function name ordinal + funclist funcs; + + uint32_t last_ordinal = ordinal_base; + //Enumerate all exported functions + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + + //If we're skipping some ordinals... + if(func.get_ordinal() > last_ordinal) + { + //Fill this function RVAs data with zeros + uint32_t len = sizeof(uint32_t) * (func.get_ordinal() - last_ordinal - 1); + if(len) + { + memset(&raw_data[current_pos_of_function_addresses], 0, len); + current_pos_of_function_addresses += len; + } + + //Save last encountered ordinal + last_ordinal = func.get_ordinal(); + } + + //If function is named, save its name ordinal and name in sorted alphabetically order + if(func.has_name()) + funcs.insert(std::make_pair(func.get_name(), static_cast<uint16_t>(func.get_ordinal() - ordinal_base))); //Calculate name ordinal + + //If function is forwarded to another DLL + if(func.is_forwarded()) + { + //Write its forwarded name and its RVA + uint32_t function_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_forwards); + memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); + current_pos_of_function_addresses += sizeof(function_rva); + + memcpy(&raw_data[current_pos_of_function_forwards], func.get_forwarded_name().c_str(), func.get_forwarded_name().length() + 1); + current_pos_of_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); + } + else + { + //Write actual function RVA + uint32_t function_rva = func.get_rva(); + memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); + current_pos_of_function_addresses += sizeof(function_rva); + } + } + + //Enumerate sorted function names + for(funclist::const_iterator it = funcs.begin(); it != funcs.end(); ++it) + { + //Save function name RVA + uint32_t function_name_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_names); + memcpy(&raw_data[current_pos_of_function_names_rvas], &function_name_rva, sizeof(function_name_rva)); + current_pos_of_function_names_rvas += sizeof(function_name_rva); + + //Save function name + memcpy(&raw_data[current_pos_of_function_names], (*it).first.c_str(), (*it).first.length() + 1); + current_pos_of_function_names += static_cast<uint32_t>((*it).first.length() + 1); + + //Save function name ordinal + uint16_t name_ordinal = (*it).second; + memcpy(&raw_data[current_pos_of_function_name_ordinals], &name_ordinal, sizeof(name_ordinal)); + current_pos_of_function_name_ordinals += sizeof(name_ordinal); + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(exports_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(exports_section, directory_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_export, ret.get_rva()); + pe.set_directory_size(image_directory_entry_export, ret.get_size()); + } + + return ret; +} +} diff --git a/pe_lib/pe_exports.h b/pe_lib/pe_exports.h new file mode 100644 index 0000000..37925e7 --- /dev/null +++ b/pe_lib/pe_exports.h @@ -0,0 +1,163 @@ +#pragma once +#include <vector> +#include <string> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing exported function +class exported_function +{ +public: + //Default constructor + exported_function(); + + //Returns ordinal of function (actually, ordinal = hint + ordinal base) + uint16_t get_ordinal() const; + + //Returns RVA of function + uint32_t get_rva() const; + + //Returns true if function has name and name ordinal + bool has_name() const; + //Returns name of function + const std::string& get_name() const; + //Returns name ordinal of function + uint16_t get_name_ordinal() const; + + //Returns true if function is forwarded to other library + bool is_forwarded() const; + //Returns the name of forwarded function + const std::string& get_forwarded_name() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild export directory + + //Sets ordinal of function + void set_ordinal(uint16_t ordinal); + + //Sets RVA of function + void set_rva(uint32_t rva); + + //Sets name of function (or clears it, if empty name is passed) + void set_name(const std::string& name); + //Sets name ordinal + void set_name_ordinal(uint16_t name_ordinal); + + //Sets forwarded function name (or clears it, if empty name is passed) + void set_forwarded_name(const std::string& name); + +private: + uint16_t ordinal_; //Function ordinal + uint32_t rva_; //Function RVA + std::string name_; //Function name + bool has_name_; //true == function has name + uint16_t name_ordinal_; //Function name ordinal + bool forward_; //true == function is forwarded + std::string forward_name_; //Name of forwarded function +}; + +//Class representing export information +class export_info +{ +public: + //Default constructor + export_info(); + + //Returns characteristics + uint32_t get_characteristics() const; + //Returns timestamp + uint32_t get_timestamp() const; + //Returns major version + uint16_t get_major_version() const; + //Returns minor version + uint16_t get_minor_version() const; + //Returns DLL name + const std::string& get_name() const; + //Returns ordinal base + uint32_t get_ordinal_base() const; + //Returns number of functions + uint32_t get_number_of_functions() const; + //Returns number of function names + uint32_t get_number_of_names() const; + //Returns RVA of function address table + uint32_t get_rva_of_functions() const; + //Returns RVA of function name address table + uint32_t get_rva_of_names() const; + //Returns RVA of name ordinals table + uint32_t get_rva_of_name_ordinals() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild export directory using rebuild_exports + + //Sets characteristics + void set_characteristics(uint32_t characteristics); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + //Sets major version + void set_major_version(uint16_t major_version); + //Sets minor version + void set_minor_version(uint16_t minor_version); + //Sets DLL name + void set_name(const std::string& name); + //Sets ordinal base + void set_ordinal_base(uint32_t ordinal_base); + //Sets number of functions + void set_number_of_functions(uint32_t number_of_functions); + //Sets number of function names + void set_number_of_names(uint32_t number_of_names); + //Sets RVA of function address table + void set_rva_of_functions(uint32_t rva_of_functions); + //Sets RVA of function name address table + void set_rva_of_names(uint32_t rva_of_names); + //Sets RVA of name ordinals table + void set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals); + +private: + uint32_t characteristics_; + uint32_t timestamp_; + uint16_t major_version_; + uint16_t minor_version_; + std::string name_; + uint32_t ordinal_base_; + uint32_t number_of_functions_; + uint32_t number_of_names_; + uint32_t address_of_functions_; + uint32_t address_of_names_; + uint32_t address_of_name_ordinals_; +}; + +//Exported functions list typedef +typedef std::vector<exported_function> exported_functions_list; + +//Returns array of exported functions +const exported_functions_list get_exported_functions(const pe_base& pe); +//Returns array of exported functions and information about export +const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info); + +//Helper export functions +//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> +const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports); + +//Checks if exported function name already exists +bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports); + +//Checks if exported function ordinal already exists +bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports); + +//Export directory rebuilder +//info - export information +//exported_functions_list - list of exported functions +//exports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from exports_section raw data start +//save_to_pe_headers - if true, new export directory information will be saved to PE image headers +//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped +//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently +//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure +//Returns new export directory information +//exported_functions_list is copied intentionally to be sorted by ordinal values later +//Name ordinals in exported function don't matter, they will be recalculated +const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/pe_lib/pe_factory.cpp b/pe_lib/pe_factory.cpp index 3d23135..446fc14 100644 --- a/pe_lib/pe_factory.cpp +++ b/pe_lib/pe_factory.cpp @@ -1,18 +1,12 @@ #include "pe_factory.h" -#include "pe_32_64.h" +#include "pe_properties_generic.h" namespace pe_bliss { -std::auto_ptr<pe_base> pe_factory::create_pe(std::istream& file, bool read_bound_import_raw_data, bool read_debug_raw_data) +pe_base pe_factory::create_pe(std::istream& file, bool read_debug_raw_data) { - std::auto_ptr<pe_base> ret; - - //Determine PE type and create corresponding class instance - ret.reset(pe_base::get_pe_type(file) == pe_base::pe_type_32 - ? static_cast<pe_base*>(new pe32(file, read_bound_import_raw_data, read_debug_raw_data)) - : static_cast<pe_base*>(new pe64(file, read_bound_import_raw_data, read_debug_raw_data)) - ); - - return ret; + return pe_base::get_pe_type(file) == pe_type_32 + ? pe_base(file, pe_properties_32(), read_debug_raw_data) + : pe_base(file, pe_properties_64(), read_debug_raw_data); } } diff --git a/pe_lib/pe_factory.h b/pe_lib/pe_factory.h index 7bdec6f..0ffb301 100644 --- a/pe_lib/pe_factory.h +++ b/pe_lib/pe_factory.h @@ -11,6 +11,6 @@ public: //Creates pe_base class instance from PE or PE+ istream //If read_bound_import_raw_data, raw bound import data will be read (used to get bound import info) //If read_debug_raw_data, raw debug data will be read (used to get image debug info) - static std::auto_ptr<pe_base> create_pe(std::istream& file, bool read_bound_import_raw_data = true, bool read_debug_raw_data = true); + static pe_base create_pe(std::istream& file, bool read_debug_raw_data = true); }; } diff --git a/pe_lib/pe_imports.cpp b/pe_lib/pe_imports.cpp new file mode 100644 index 0000000..bb45e38 --- /dev/null +++ b/pe_lib/pe_imports.cpp @@ -0,0 +1,756 @@ +#include <string.h> +#include "pe_imports.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//IMPORTS +//Default constructor +//If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset +//to new value after import rebuilding +//If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero +//IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable +//to be able to modify IAT thunks +import_rebuilder_settings::import_rebuilder_settings(bool set_to_pe_headers, bool auto_zero_directory_entry_iat) + :offset_from_section_start_(0), + build_original_iat_(true), + save_iat_and_original_iat_rvas_(true), + fill_missing_original_iats_(false), + set_to_pe_headers_(set_to_pe_headers), + zero_directory_entry_iat_(auto_zero_directory_entry_iat), + rewrite_iat_and_original_iat_contents_(false), + auto_strip_last_section_(true) +{} + +//Returns offset from section start where import directory data will be placed +uint32_t import_rebuilder_settings::get_offset_from_section_start() const +{ + return offset_from_section_start_; +} + +//Returns true if Original import address table (IAT) will be rebuilt +bool import_rebuilder_settings::build_original_iat() const +{ + return build_original_iat_; +} + +//Returns true if Original import address and import address tables will not be rebuilt, +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +bool import_rebuilder_settings::save_iat_and_original_iat_rvas() const +{ + return save_iat_and_original_iat_rvas_; +} + +//Returns true if Original import address and import address tables contents will be rewritten +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +//and save_iat_and_original_iat_rvas is true +bool import_rebuilder_settings::rewrite_iat_and_original_iat_contents() const +{ + return rewrite_iat_and_original_iat_contents_; +} + +//Returns true if original missing IATs will be rebuilt +//(only if IATs are saved) +bool import_rebuilder_settings::fill_missing_original_iats() const +{ + return fill_missing_original_iats_; +} + +//Returns true if PE headers should be updated automatically after rebuilding of imports +bool import_rebuilder_settings::auto_set_to_pe_headers() const +{ + return set_to_pe_headers_; +} + +//Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true +bool import_rebuilder_settings::zero_directory_entry_iat() const +{ + return zero_directory_entry_iat_; +} + +//Returns true if the last section should be stripped automatically, if imports are inside it +bool import_rebuilder_settings::auto_strip_last_section_enabled() const +{ + return auto_strip_last_section_; +} + +//Sets offset from section start where import directory data will be placed +void import_rebuilder_settings::set_offset_from_section_start(uint32_t offset) +{ + offset_from_section_start_ = offset; +} + +//Sets if Original import address table (IAT) will be rebuilt +void import_rebuilder_settings::build_original_iat(bool enable) +{ + build_original_iat_ = enable; +} + +//Sets if Original import address and import address tables will not be rebuilt, +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +void import_rebuilder_settings::save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents) +{ + save_iat_and_original_iat_rvas_ = enable; + if(save_iat_and_original_iat_rvas_) + rewrite_iat_and_original_iat_contents_ = enable_rewrite_iat_and_original_iat_contents; + else + rewrite_iat_and_original_iat_contents_ = false; +} + +//Sets if original missing IATs will be rebuilt +//(only if IATs are saved) +void import_rebuilder_settings::fill_missing_original_iats(bool enable) +{ + fill_missing_original_iats_ = enable; +} + +//Sets if PE headers should be updated automatically after rebuilding of imports +void import_rebuilder_settings::auto_set_to_pe_headers(bool enable) +{ + set_to_pe_headers_ = enable; +} + +//Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true +void import_rebuilder_settings::zero_directory_entry_iat(bool enable) +{ + zero_directory_entry_iat_ = enable; +} + +//Sets if the last section should be stripped automatically, if imports are inside it, default true +void import_rebuilder_settings::enable_auto_strip_last_section(bool enable) +{ + auto_strip_last_section_ = enable; +} + +//Default constructor +imported_function::imported_function() + :hint_(0), ordinal_(0), iat_va_(0) +{} + +//Returns name of function +const std::string& imported_function::get_name() const +{ + return name_; +} + +//Returns true if imported function has name (and hint) +bool imported_function::has_name() const +{ + return !name_.empty(); +} + +//Returns hint +uint16_t imported_function::get_hint() const +{ + return hint_; +} + +//Returns ordinal of function +uint16_t imported_function::get_ordinal() const +{ + return ordinal_; +} + +//Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) +uint64_t imported_function::get_iat_va() const +{ + return iat_va_; +} + +//Sets name of function +void imported_function::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets hint +void imported_function::set_hint(uint16_t hint) +{ + hint_ = hint; +} + +//Sets ordinal +void imported_function::set_ordinal(uint16_t ordinal) +{ + ordinal_ = ordinal; +} + +//Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) +void imported_function::set_iat_va(uint64_t va) +{ + iat_va_ = va; +} + +//Default constructor +import_library::import_library() + :rva_to_iat_(0), rva_to_original_iat_(0), timestamp_(0) +{} + +//Returns name of library +const std::string& import_library::get_name() const +{ + return name_; +} + +//Returns RVA to Import Address Table (IAT) +uint32_t import_library::get_rva_to_iat() const +{ + return rva_to_iat_; +} + +//Returns RVA to Original Import Address Table (Original IAT) +uint32_t import_library::get_rva_to_original_iat() const +{ + return rva_to_original_iat_; +} + +//Returns timestamp +uint32_t import_library::get_timestamp() const +{ + return timestamp_; +} + +//Sets name of library +void import_library::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets RVA to Import Address Table (IAT) +void import_library::set_rva_to_iat(uint32_t rva_to_iat) +{ + rva_to_iat_ = rva_to_iat; +} + +//Sets RVA to Original Import Address Table (Original IAT) +void import_library::set_rva_to_original_iat(uint32_t rva_to_original_iat) +{ + rva_to_original_iat_ = rva_to_original_iat; +} + +//Sets timestamp +void import_library::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Returns imported functions list +const import_library::imported_list& import_library::get_imported_functions() const +{ + return imports_; +} + +//Adds imported function +void import_library::add_import(const imported_function& func) +{ + imports_.push_back(func); +} + +//Clears imported functions list +void import_library::clear_imports() +{ + imports_.clear(); +} + +const imported_functions_list get_imported_functions(const pe_base& pe) +{ + return (pe.get_pe_type() == pe_type_32 ? + get_imported_functions_base<pe_types_class_32>(pe) + : get_imported_functions_base<pe_types_class_64>(pe)); +} + +const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings) +{ + return (pe.get_pe_type() == pe_type_32 ? + rebuild_imports_base<pe_types_class_32>(pe, imports, import_section, import_settings) + : rebuild_imports_base<pe_types_class_64>(pe, imports, import_section, import_settings)); +} + +//Returns imported functions list with related libraries info +template<typename PEClassType> +const imported_functions_list get_imported_functions_base(const pe_base& pe) +{ + imported_functions_list ret; + + //If image has no imports, return empty array + if(!pe.has_imports()) + return ret; + + unsigned long current_descriptor_pos = pe.get_directory_rva(image_directory_entry_import); + //Get first IMAGE_IMPORT_DESCRIPTOR + image_import_descriptor import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true); + + //Iterate them until we reach zero-element + //We don't need to check correctness of this, because exception will be thrown + //inside of loop if we go outsize of section + while(import_descriptor.Name) + { + //Get imported library information + import_library lib; + + unsigned long max_name_length; + //Get byte count that we have for library name + if((max_name_length = pe.section_data_length_from_rva(import_descriptor.Name, import_descriptor.Name, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get DLL name pointer + const char* dll_name = pe.section_data_from_rva(import_descriptor.Name, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(dll_name, max_name_length)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Set library name + lib.set_name(dll_name); + //Set library timestamp + lib.set_timestamp(import_descriptor.TimeDateStamp); + //Set library RVA to IAT and original IAT + lib.set_rva_to_iat(import_descriptor.FirstThunk); + lib.set_rva_to_original_iat(import_descriptor.OriginalFirstThunk); + + //Get RVA to IAT (it must be filled by loader when loading PE) + uint32_t current_thunk_rva = import_descriptor.FirstThunk; + typename PEClassType::BaseSize import_address_table = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true); + + //Get RVA to original IAT (lookup table), which must handle imported functions names + //Some linkers leave this pointer zero-filled + //Such image is valid, but it is not possible to restore imported functions names + //afted image was loaded, because IAT becomes the only one table + //containing both function names and function RVAs after loading + uint32_t current_original_thunk_rva = import_descriptor.OriginalFirstThunk; + typename PEClassType::BaseSize import_lookup_table = current_original_thunk_rva == 0 ? import_address_table : pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true); + if(current_original_thunk_rva == 0) + current_original_thunk_rva = current_thunk_rva; + + //List all imported functions for current DLL + if(import_lookup_table != 0 && import_address_table != 0) + { + while(true) + { + //Imported function description + imported_function func; + + //Get VA from IAT + typename PEClassType::BaseSize address = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true); + //Move pointer + current_thunk_rva += sizeof(typename PEClassType::BaseSize); + + //Jump to next DLL if we finished with this one + if(!address) + break; + + func.set_iat_va(address); + + //Get VA from original IAT + typename PEClassType::BaseSize lookup = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true); + //Move pointer + current_original_thunk_rva += sizeof(typename PEClassType::BaseSize); + + //Check if function is imported by ordinal + if((lookup & PEClassType::ImportSnapFlag) != 0) + { + //Set function ordinal + func.set_ordinal(static_cast<uint16_t>(lookup & 0xffff)); + } + else + { + //Get byte count that we have for function name + if(lookup > static_cast<uint32_t>(-1) - sizeof(uint16_t)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get maximum available length of function name + if((max_name_length = pe.section_data_length_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true)) < 2) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get imported function name + const char* func_name = pe.section_data_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(func_name, max_name_length)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //HINT in import table is ORDINAL in export table + uint16_t hint = pe.section_data_from_rva<uint16_t>(static_cast<uint32_t>(lookup), section_data_virtual, true); + + //Save hint and name + func.set_name(func_name); + func.set_hint(hint); + } + + //Add function to list + lib.add_import(func); + } + } + + //Check possible overflow + if(!pe_utils::is_sum_safe(current_descriptor_pos, sizeof(image_import_descriptor))) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Go to next library + current_descriptor_pos += sizeof(image_import_descriptor); + import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true); + + //Save import information + ret.push_back(lib); + } + + //Return resulting list + return ret; +} + + +//Simple import directory rebuilder +//You can get all image imports with get_imported_functions() function +//You can use returned value to, for example, add new imported library with some functions +//to the end of list of imported libraries +//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default) +//Don't add new imported functions to existing imported library entries, because this can cause +//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader +//The safest way is just adding import libraries with functions to the end of imported_functions_list array +template<typename PEClassType> +const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings) +{ + //Check that import_section is attached to this PE image + if(!pe.section_attached(import_section)) + throw pe_exception("Import section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t needed_size = 0; //Calculate needed size for import structures and strings + uint32_t needed_size_for_strings = 0; //Calculate needed size for import strings (library and function names and hints) + uint32_t size_of_iat = 0; //Size of IAT structures + + needed_size += static_cast<uint32_t>((1 /* ending null descriptor */ + imports.size()) * sizeof(image_import_descriptor)); + + //Enumerate imported functions + for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + needed_size_for_strings += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */); + + const import_library::imported_list& funcs = (*it).get_imported_functions(); + + //IMAGE_THUNK_DATA + size_of_iat += static_cast<uint32_t>(sizeof(typename PEClassType::BaseSize) * (1 /*ending null */ + funcs.size())); + + //Enumerate all imported functions in library + for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f) + { + if((*f).has_name()) + needed_size_for_strings += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */); + } + } + + if(import_settings.build_original_iat() || import_settings.fill_missing_original_iats()) + needed_size += size_of_iat * 2; //We'll have two similar-sized IATs if we're building original IAT + else + needed_size += size_of_iat; + + needed_size += sizeof(typename PEClassType::BaseSize); //Maximum align for IAT and original IAT + + //Total needed size for import structures and strings + needed_size += needed_size_for_strings; + + //Check if import_section is last one. If it's not, check if there's enough place for import data + if(&import_section != &*(pe.get_image_sections().end() - 1) && + (import_section.empty() || pe_utils::align_up(import_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + import_settings.get_offset_from_section_start())) + throw pe_exception("Insufficient space for import directory", pe_exception::insufficient_space); + + std::string& raw_data = import_section.get_raw_data(); + + //This will be done only if image_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + import_settings.get_offset_from_section_start()) + raw_data.resize(needed_size + import_settings.get_offset_from_section_start()); //Expand section raw data + + uint32_t current_string_pointer = import_settings.get_offset_from_section_start();/* we will paste structures after strings */ + + //Position for IAT + uint32_t current_pos_for_iat = pe_utils::align_up(static_cast<uint32_t>(needed_size_for_strings + import_settings.get_offset_from_section_start() + (1 + imports.size()) * sizeof(image_import_descriptor)), sizeof(typename PEClassType::BaseSize)); + //Position for original IAT + uint32_t current_pos_for_original_iat = current_pos_for_iat + size_of_iat; + //Position for import descriptors + uint32_t current_pos_for_descriptors = needed_size_for_strings + import_settings.get_offset_from_section_start(); + + //Build imports + for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + //Create import descriptor + image_import_descriptor descr; + memset(&descr, 0, sizeof(descr)); + descr.TimeDateStamp = (*it).get_timestamp(); //Restore timestamp + descr.Name = pe.rva_from_section_offset(import_section, current_string_pointer); //Library name RVA + + //If we should save IAT for current import descriptor + bool save_iats_for_this_descriptor = import_settings.save_iat_and_original_iat_rvas() && (*it).get_rva_to_iat() != 0; + //If we should write original IAT + bool write_original_iat = (!save_iats_for_this_descriptor && import_settings.build_original_iat()) || import_settings.fill_missing_original_iats(); + + //If we should rewrite saved original IAT for current import descriptor (without changing its position) + bool rewrite_saved_original_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && import_settings.build_original_iat(); + //If we should rewrite saved IAT for current import descriptor (without changing its position) + bool rewrite_saved_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && (*it).get_rva_to_iat() != 0; + + //Helper values if we're rewriting existing IAT or orig.IAT + uint32_t original_first_thunk = 0; + uint32_t first_thunk = 0; + + if(save_iats_for_this_descriptor) + { + //If there's no original IAT and we're asked to rebuild missing original IATs + if(!(*it).get_rva_to_original_iat() && import_settings.fill_missing_original_iats()) + descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0; + else + descr.OriginalFirstThunk = import_settings.build_original_iat() ? (*it).get_rva_to_original_iat() : 0; + + descr.FirstThunk = (*it).get_rva_to_iat(); + + original_first_thunk = descr.OriginalFirstThunk; + first_thunk = descr.FirstThunk; + + if(rewrite_saved_original_iat) + { + if((*it).get_rva_to_original_iat()) + write_original_iat = true; + else + rewrite_saved_original_iat = false; + } + + if(rewrite_saved_iat) + save_iats_for_this_descriptor = false; + } + else + { + //We are creating new IAT and original IAT (if needed) + descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0; + descr.FirstThunk = pe.rva_from_section_offset(import_section, current_pos_for_iat); + } + + //Save import descriptor + memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr)); + current_pos_for_descriptors += sizeof(descr); + + //Save library name + memcpy(&raw_data[current_string_pointer], (*it).get_name().c_str(), (*it).get_name().length() + 1 /* nullbyte */); + current_string_pointer += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */); + + //List all imported functions + const import_library::imported_list& funcs = (*it).get_imported_functions(); + for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f) + { + if((*f).has_name()) //If function is imported by name + { + //Get RVA of IMAGE_IMPORT_BY_NAME + typename PEClassType::BaseSize rva_of_named_import = pe.rva_from_section_offset(import_section, current_string_pointer); + + if(!save_iats_for_this_descriptor) + { + if(write_original_iat) + { + //We're creating original IATs - so we can write to IAT saved VA (because IMAGE_IMPORT_BY_NAME will be read + //by PE loader from original IAT) + typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va()); + + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value)); + + first_thunk += sizeof(iat_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value)); + current_pos_for_iat += sizeof(rva_of_named_import); + } + } + else + { + //Else - write to IAT RVA of IMAGE_IMPORT_BY_NAME + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import)); + + first_thunk += sizeof(rva_of_named_import); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &rva_of_named_import, sizeof(rva_of_named_import)); + current_pos_for_iat += sizeof(rva_of_named_import); + } + } + } + + if(write_original_iat) + { + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import)); + + original_first_thunk += sizeof(rva_of_named_import); + } + else + { + //We're creating original IATs + memcpy(&raw_data[current_pos_for_original_iat], &rva_of_named_import, sizeof(rva_of_named_import)); + current_pos_for_original_iat += sizeof(rva_of_named_import); + } + } + + //Write IMAGE_IMPORT_BY_NAME (WORD hint + string function name) + uint16_t hint = (*f).get_hint(); + memcpy(&raw_data[current_string_pointer], &hint, sizeof(hint)); + memcpy(&raw_data[current_string_pointer + sizeof(uint16_t)], (*f).get_name().c_str(), (*f).get_name().length() + 1 /* nullbyte */); + current_string_pointer += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */); + } + else //Function is imported by ordinal + { + uint16_t ordinal = (*f).get_ordinal(); + typename PEClassType::BaseSize thunk_value = ordinal; + thunk_value |= PEClassType::ImportSnapFlag; //Imported by ordinal + + if(!save_iats_for_this_descriptor) + { + if(write_original_iat) + { + //We're creating original IATs - so we can wtire to IAT saved VA (because ordinal will be read + //by PE loader from original IAT) + typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va()); + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value)); + + first_thunk += sizeof(iat_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value)); + current_pos_for_iat += sizeof(thunk_value); + } + } + else + { + //Else - write ordinal to IAT + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value)); + + first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value)); + } + } + } + + //We're writing ordinal to original IAT slot + if(write_original_iat) + { + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value)); + + original_first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_original_iat += sizeof(thunk_value); + } + } + } + } + + if(!save_iats_for_this_descriptor) + { + //Ending null thunks + typename PEClassType::BaseSize thunk_value = 0; + + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value)); + + first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_iat += sizeof(thunk_value); + } + } + + if(write_original_iat) + { + //Ending null thunks + typename PEClassType::BaseSize thunk_value = 0; + + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value)); + + original_first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_original_iat += sizeof(thunk_value); + } + } + } + + { + //Null ending descriptor + image_import_descriptor descr; + memset(&descr, 0, sizeof(descr)); + memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr)); + } + + //Strip data a little, if we saved some place + //We're allocating more space than needed, if present original IAT and IAT are saved + raw_data.resize(current_pos_for_original_iat); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(import_section, import_settings.auto_strip_last_section_enabled()); + + //Return information about rebuilt import directory + image_directory ret(pe.rva_from_section_offset(import_section, import_settings.get_offset_from_section_start() + needed_size_for_strings), needed_size - needed_size_for_strings); + + //If auto-rewrite of PE headers is required + if(import_settings.auto_set_to_pe_headers()) + { + pe.set_directory_rva(image_directory_entry_import, ret.get_rva()); + pe.set_directory_size(image_directory_entry_import, ret.get_size()); + + //If we are requested to zero IMAGE_DIRECTORY_ENTRY_IAT also + if(import_settings.zero_directory_entry_iat()) + { + pe.set_directory_rva(image_directory_entry_iat, 0); + pe.set_directory_size(image_directory_entry_iat, 0); + } + } + + return ret; +} +} diff --git a/pe_lib/pe_imports.h b/pe_lib/pe_imports.h new file mode 100644 index 0000000..5e63790 --- /dev/null +++ b/pe_lib/pe_imports.h @@ -0,0 +1,187 @@ +#pragma once +#include <vector> +#include <string> +#include "pe_structures.h" +#include "pe_directory.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing imported function +class imported_function +{ +public: + //Default constructor + imported_function(); + + //Returns true if imported function has name (and hint) + bool has_name() const; + //Returns name of function + const std::string& get_name() const; + //Returns hint + uint16_t get_hint() const; + //Returns ordinal of function + uint16_t get_ordinal() const; + + //Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) + uint64_t get_iat_va() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You also can use them to rebuild image imports + //Sets name of function + void set_name(const std::string& name); + //Sets hint + void set_hint(uint16_t hint); + //Sets ordinal + void set_ordinal(uint16_t ordinal); + + //Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) + void set_iat_va(uint64_t rva); + +private: + std::string name_; //Function name + uint16_t hint_; //Hint + uint16_t ordinal_; //Ordinal + uint64_t iat_va_; +}; + +//Class representing imported library information +class import_library +{ +public: + typedef std::vector<imported_function> imported_list; + +public: + //Default constructor + import_library(); + + //Returns name of library + const std::string& get_name() const; + //Returns RVA to Import Address Table (IAT) + uint32_t get_rva_to_iat() const; + //Returns RVA to Original Import Address Table (Original IAT) + uint32_t get_rva_to_original_iat() const; + //Returns timestamp + uint32_t get_timestamp() const; + + //Returns imported functions list + const imported_list& get_imported_functions() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You also can use them to rebuild image imports + //Sets name of library + void set_name(const std::string& name); + //Sets RVA to Import Address Table (IAT) + void set_rva_to_iat(uint32_t rva_to_iat); + //Sets RVA to Original Import Address Table (Original IAT) + void set_rva_to_original_iat(uint32_t rva_to_original_iat); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + + //Adds imported function + void add_import(const imported_function& func); + //Clears imported functions list + void clear_imports(); + +private: + std::string name_; //Library name + uint32_t rva_to_iat_; //RVA to IAT + uint32_t rva_to_original_iat_; //RVA to original IAT + uint32_t timestamp_; //DLL TimeStamp + + imported_list imports_; +}; + +//Simple import directory rebuilder +//Class representing import rebuilder advanced settings +class import_rebuilder_settings +{ +public: + //Default constructor + //Default constructor + //If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset + //to new value after import rebuilding + //If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero + //IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable + //to be able to modify IAT thunks + explicit import_rebuilder_settings(bool set_to_pe_headers = true, bool auto_zero_directory_entry_iat = false); + + //Returns offset from section start where import directory data will be placed + uint32_t get_offset_from_section_start() const; + //Returns true if Original import address table (IAT) will be rebuilt + bool build_original_iat() const; + + //Returns true if Original import address and import address tables will not be rebuilt, + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + bool save_iat_and_original_iat_rvas() const; + //Returns true if Original import address and import address tables contents will be rewritten + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //and save_iat_and_original_iat_rvas is true + bool rewrite_iat_and_original_iat_contents() const; + + //Returns true if original missing IATs will be rebuilt + //(only if IATs are saved) + bool fill_missing_original_iats() const; + //Returns true if PE headers should be updated automatically after rebuilding of imports + bool auto_set_to_pe_headers() const; + //Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true + bool zero_directory_entry_iat() const; + + //Returns true if the last section should be stripped automatically, if imports are inside it + bool auto_strip_last_section_enabled() const; + +public: //Setters + //Sets offset from section start where import directory data will be placed + void set_offset_from_section_start(uint32_t offset); + //Sets if Original import address table (IAT) will be rebuilt + void build_original_iat(bool enable); + //Sets if Original import address and import address tables will not be rebuilt, + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //enable_rewrite_iat_and_original_iat_contents sets if Original import address and import address tables contents will be rewritten + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //and save_iat_and_original_iat_rvas is true + void save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents = false); + //Sets if original missing IATs will be rebuilt + //(only if IATs are saved) + void fill_missing_original_iats(bool enable); + //Sets if PE headers should be updated automatically after rebuilding of imports + void auto_set_to_pe_headers(bool enable); + //Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true + void zero_directory_entry_iat(bool enable); + + //Sets if the last section should be stripped automatically, if imports are inside it, default true + void enable_auto_strip_last_section(bool enable); + +private: + uint32_t offset_from_section_start_; + bool build_original_iat_; + bool save_iat_and_original_iat_rvas_; + bool fill_missing_original_iats_; + bool set_to_pe_headers_; + bool zero_directory_entry_iat_; + bool rewrite_iat_and_original_iat_contents_; + bool auto_strip_last_section_; +}; + +typedef std::vector<import_library> imported_functions_list; + + +//Returns imported functions list with related libraries info +const imported_functions_list get_imported_functions(const pe_base& pe); + +template<typename PEClassType> +const imported_functions_list get_imported_functions_base(const pe_base& pe); + + +//You can get all image imports with get_imported_functions() function +//You can use returned value to, for example, add new imported library with some functions +//to the end of list of imported libraries +//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default) +//Don't add new imported functions to existing imported library entries, because this can cause +//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader +//The safest way is just adding import libraries with functions to the end of imported_functions_list array +const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings()); + +template<typename PEClassType> +const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings()); +} diff --git a/pe_lib/pe_lib.vcproj b/pe_lib/pe_lib.vcproj index 26d65f0..bea5754 100644 --- a/pe_lib/pe_lib.vcproj +++ b/pe_lib/pe_lib.vcproj @@ -2,9 +2,9 @@ <VisualStudioProject ProjectType="Visual C++" Version="9,00" - Name="pe_lib" + Name="pe_bliss" ProjectGUID="{4B658F8F-1722-4EEA-880C-A4A64DCA9F2C}" - RootNamespace="pe_lib" + RootNamespace="pe_bliss" Keyword="Win32Proj" TargetFrameworkVersion="196613" > @@ -81,12 +81,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="4" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -102,14 +101,15 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" - PreprocessorDefinitions="WIN32;NDEBUG;_LIB" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + Optimization="0" + PreprocessorDefinitions="_WIN64;_DEBUG;_LIB" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -143,11 +143,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="4" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -163,15 +164,14 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" - PreprocessorDefinitions="WIN32;_DEBUG;_LIB" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + Optimization="2" + EnableIntrinsicFunctions="true" + PreprocessorDefinitions="WIN32;NDEBUG;_LIB" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -232,7 +232,7 @@ Name="VCCLCompilerTool" Optimization="2" EnableIntrinsicFunctions="true" - PreprocessorDefinitions="WIN32;NDEBUG;_LIB" + PreprocessorDefinitions="_WIN64;NDEBUG;_LIB" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" @@ -276,10 +276,6 @@ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > - <File - RelativePath=".\pe_32_64.cpp" - > - </File> <File RelativePath=".\pe_base.cpp" > @@ -293,9 +289,153 @@ > </File> <File - RelativePath=".\pe_resource_manager.cpp" + RelativePath=".\pe_properties.cpp" > </File> + <File + RelativePath=".\pe_properties_generic.cpp" + > + </File> + <File + RelativePath=".\pe_rebuilder.cpp" + > + </File> + <File + RelativePath=".\pe_section.cpp" + > + </File> + <Filter + Name="Other" + > + <File + RelativePath=".\entropy.cpp" + > + </File> + <File + RelativePath=".\pe_checksum.cpp" + > + </File> + <File + RelativePath=".\pe_rich_data.cpp" + > + </File> + <File + RelativePath=".\utils.cpp" + > + </File> + </Filter> + <Filter + Name="PE Resources" + > + <File + RelativePath=".\file_version_info.cpp" + > + </File> + <File + RelativePath=".\message_table.cpp" + > + </File> + <File + RelativePath=".\pe_resource_manager.cpp" + > + </File> + <File + RelativePath=".\pe_resource_viewer.cpp" + > + </File> + <File + RelativePath=".\resource_bitmap_reader.cpp" + > + </File> + <File + RelativePath=".\resource_bitmap_writer.cpp" + > + </File> + <File + RelativePath=".\resource_cursor_icon_reader.cpp" + > + </File> + <File + RelativePath=".\resource_cursor_icon_writer.cpp" + > + </File> + <File + RelativePath=".\resource_data_info.cpp" + > + </File> + <File + RelativePath=".\resource_message_list_reader.cpp" + > + </File> + <File + RelativePath=".\resource_string_table_reader.cpp" + > + </File> + <File + RelativePath=".\resource_version_info_reader.cpp" + > + </File> + <File + RelativePath=".\resource_version_info_writer.cpp" + > + </File> + <File + RelativePath=".\version_info_editor.cpp" + > + </File> + <File + RelativePath=".\version_info_viewer.cpp" + > + </File> + </Filter> + <Filter + Name="PE Directories" + > + <File + RelativePath=".\pe_bound_import.cpp" + > + </File> + <File + RelativePath=".\pe_debug.cpp" + > + </File> + <File + RelativePath=".\pe_directory.cpp" + > + </File> + <File + RelativePath=".\pe_dotnet.cpp" + > + </File> + <File + RelativePath=".\pe_exception_directory.cpp" + > + </File> + <File + RelativePath=".\pe_exports.cpp" + > + </File> + <File + RelativePath=".\pe_imports.cpp" + > + </File> + <File + RelativePath=".\pe_load_config.cpp" + > + </File> + <File + RelativePath=".\pe_relocations.cpp" + > + </File> + <File + RelativePath=".\pe_resources.cpp" + > + </File> + <File + RelativePath=".\pe_tls.cpp" + > + </File> + </Filter> </Filter> <Filter Name="Header Files" @@ -303,11 +443,15 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > <File - RelativePath=".\pe_32_64.h" + RelativePath=".\pe_base.h" > </File> <File - RelativePath=".\pe_base.h" + RelativePath=".\pe_bliss.h" + > + </File> + <File + RelativePath=".\pe_bliss_resources.h" > </File> <File @@ -319,7 +463,19 @@ > </File> <File - RelativePath=".\pe_resource_manager.h" + RelativePath=".\pe_properties.h" + > + </File> + <File + RelativePath=".\pe_properties_generic.h" + > + </File> + <File + RelativePath=".\pe_rebuilder.h" + > + </File> + <File + RelativePath=".\pe_section.h" > </File> <File @@ -330,6 +486,142 @@ RelativePath=".\stdint_defs.h" > </File> + <Filter + Name="Other" + > + <File + RelativePath=".\entropy.h" + > + </File> + <File + RelativePath=".\pe_checksum.h" + > + </File> + <File + RelativePath=".\pe_rich_data.h" + > + </File> + <File + RelativePath=".\utils.h" + > + </File> + </Filter> + <Filter + Name="PE Resources" + > + <File + RelativePath=".\file_version_info.h" + > + </File> + <File + RelativePath=".\message_table.h" + > + </File> + <File + RelativePath=".\pe_resource_manager.h" + > + </File> + <File + RelativePath=".\pe_resource_viewer.h" + > + </File> + <File + RelativePath=".\resource_bitmap_reader.h" + > + </File> + <File + RelativePath=".\resource_bitmap_writer.h" + > + </File> + <File + RelativePath=".\resource_cursor_icon_reader.h" + > + </File> + <File + RelativePath=".\resource_cursor_icon_writer.h" + > + </File> + <File + RelativePath=".\resource_data_info.h" + > + </File> + <File + RelativePath=".\resource_internal.h" + > + </File> + <File + RelativePath=".\resource_message_list_reader.h" + > + </File> + <File + RelativePath=".\resource_string_table_reader.h" + > + </File> + <File + RelativePath=".\resource_version_info_reader.h" + > + </File> + <File + RelativePath=".\resource_version_info_writer.h" + > + </File> + <File + RelativePath=".\version_info_editor.h" + > + </File> + <File + RelativePath=".\version_info_viewer.h" + > + </File> + </Filter> + <Filter + Name="PE Directories" + > + <File + RelativePath=".\pe_bound_import.h" + > + </File> + <File + RelativePath=".\pe_debug.h" + > + </File> + <File + RelativePath=".\pe_directory.h" + > + </File> + <File + RelativePath=".\pe_dotnet.h" + > + </File> + <File + RelativePath=".\pe_exception_directory.h" + > + </File> + <File + RelativePath=".\pe_exports.h" + > + </File> + <File + RelativePath=".\pe_imports.h" + > + </File> + <File + RelativePath=".\pe_load_config.h" + > + </File> + <File + RelativePath=".\pe_relocations.h" + > + </File> + <File + RelativePath=".\pe_resources.h" + > + </File> + <File + RelativePath=".\pe_tls.h" + > + </File> + </Filter> </Filter> <Filter Name="Resource Files" diff --git a/pe_lib/pe_lib.vcxproj b/pe_lib/pe_lib.vcxproj index 221d66e..2fdea30 100644 --- a/pe_lib/pe_lib.vcxproj +++ b/pe_lib/pe_lib.vcxproj @@ -19,20 +19,88 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> - <ClCompile Include="pe_32_64.cpp" /> + <ClCompile Include="entropy.cpp" /> + <ClCompile Include="file_version_info.cpp" /> + <ClCompile Include="message_table.cpp" /> + <ClCompile Include="pe_bound_import.cpp" /> + <ClCompile Include="pe_checksum.cpp" /> + <ClCompile Include="pe_directory.cpp" /> + <ClCompile Include="pe_load_config.cpp" /> + <ClCompile Include="pe_properties.cpp" /> + <ClCompile Include="pe_properties_generic.cpp" /> + <ClCompile Include="pe_rebuilder.cpp" /> + <ClCompile Include="pe_resource_viewer.cpp" /> + <ClCompile Include="pe_section.cpp" /> + <ClCompile Include="pe_tls.cpp" /> + <ClCompile Include="pe_debug.cpp" /> + <ClCompile Include="pe_dotnet.cpp" /> + <ClCompile Include="pe_exception_directory.cpp" /> + <ClCompile Include="pe_exports.cpp" /> + <ClCompile Include="pe_imports.cpp" /> <ClCompile Include="pe_base.cpp" /> <ClCompile Include="pe_exception.cpp" /> <ClCompile Include="pe_factory.cpp" /> <ClCompile Include="pe_resource_manager.cpp" /> + <ClCompile Include="pe_relocations.cpp" /> + <ClCompile Include="pe_resources.cpp" /> + <ClCompile Include="pe_rich_data.cpp" /> + <ClCompile Include="resource_bitmap_reader.cpp" /> + <ClCompile Include="resource_bitmap_writer.cpp" /> + <ClCompile Include="resource_cursor_icon_writer.cpp" /> + <ClCompile Include="resource_data_info.cpp" /> + <ClCompile Include="resource_cursor_icon_reader.cpp" /> + <ClCompile Include="resource_message_list_reader.cpp" /> + <ClCompile Include="resource_string_table_reader.cpp" /> + <ClCompile Include="resource_version_info_reader.cpp" /> + <ClCompile Include="resource_version_info_writer.cpp" /> + <ClCompile Include="utils.cpp" /> + <ClCompile Include="version_info_editor.cpp" /> + <ClCompile Include="version_info_viewer.cpp" /> </ItemGroup> <ItemGroup> - <ClInclude Include="pe_32_64.h" /> + <ClInclude Include="entropy.h" /> + <ClInclude Include="file_version_info.h" /> + <ClInclude Include="message_table.h" /> + <ClInclude Include="pe_bliss_resources.h" /> + <ClInclude Include="pe_bound_import.h" /> + <ClInclude Include="pe_checksum.h" /> + <ClInclude Include="pe_debug.h" /> + <ClInclude Include="pe_directory.h" /> + <ClInclude Include="pe_dotnet.h" /> + <ClInclude Include="pe_exception_directory.h" /> + <ClInclude Include="pe_exports.h" /> + <ClInclude Include="pe_imports.h" /> <ClInclude Include="pe_base.h" /> + <ClInclude Include="pe_bliss.h" /> <ClInclude Include="pe_exception.h" /> <ClInclude Include="pe_factory.h" /> + <ClInclude Include="pe_load_config.h" /> + <ClInclude Include="pe_properties.h" /> + <ClInclude Include="pe_properties_generic.h" /> + <ClInclude Include="pe_rebuilder.h" /> <ClInclude Include="pe_resource_manager.h" /> + <ClInclude Include="pe_resource_viewer.h" /> + <ClInclude Include="pe_section.h" /> <ClInclude Include="pe_structures.h" /> + <ClInclude Include="pe_relocations.h" /> + <ClInclude Include="pe_resources.h" /> + <ClInclude Include="pe_rich_data.h" /> + <ClInclude Include="pe_tls.h" /> + <ClInclude Include="resource_bitmap_reader.h" /> + <ClInclude Include="resource_bitmap_writer.h" /> + <ClInclude Include="resource_data_info.h" /> + <ClInclude Include="resource_cursor_icon_reader.h" /> + <ClInclude Include="resource_cursor_icon_writer.h" /> + <ClInclude Include="resource_internal.h" /> + <ClInclude Include="resource_message_list_reader.h" /> + <ClInclude Include="resource_string_table_reader.h" /> + <ClInclude Include="resource_version_info_reader.h" /> + <ClInclude Include="resource_version_info_writer.h" /> <ClInclude Include="stdint_defs.h" /> + <ClInclude Include="utils.h" /> + <ClInclude Include="version_info_editor.h" /> + <ClInclude Include="version_info_types.h" /> + <ClInclude Include="version_info_viewer.h" /> </ItemGroup> <ItemGroup> <None Include="readme.txt" /> @@ -109,7 +177,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> </ClCompile> @@ -144,7 +212,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> <Link> diff --git a/pe_lib/pe_lib.vcxproj.filters b/pe_lib/pe_lib.vcxproj.filters index 7ec0d7f..6affcad 100644 --- a/pe_lib/pe_lib.vcxproj.filters +++ b/pe_lib/pe_lib.vcxproj.filters @@ -13,11 +13,26 @@ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> </Filter> + <Filter Include="Header Files\Other"> + <UniqueIdentifier>{a442c791-435d-43d3-9545-28196fb75dd9}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\PE Directories"> + <UniqueIdentifier>{f1eddfb5-ad88-4a6f-8516-031af6cbcff1}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\PE Resources"> + <UniqueIdentifier>{5e3db017-30ee-4ba1-878c-9e1fe4155555}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\PE Resources"> + <UniqueIdentifier>{9f2f9f68-41ae-41a6-b8ae-74eb7b966c8f}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\Other"> + <UniqueIdentifier>{58541054-25c2-412f-93c4-c6f3d8da7c70}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\PE Directories"> + <UniqueIdentifier>{8429c41b-f602-42fb-994f-0fa4ee6f0601}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> - <ClCompile Include="pe_32_64.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="pe_base.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -27,14 +42,110 @@ <ClCompile Include="pe_factory.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="pe_section.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="pe_debug.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="pe_directory.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="pe_dotnet.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="pe_exception_directory.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="pe_exports.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="pe_imports.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="pe_load_config.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="pe_relocations.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="pe_resources.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="pe_tls.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> <ClCompile Include="pe_resource_manager.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="pe_rich_data.cpp"> + <Filter>Source Files\Other</Filter> + </ClCompile> + <ClCompile Include="entropy.cpp"> + <Filter>Source Files\Other</Filter> + </ClCompile> + <ClCompile Include="utils.cpp"> + <Filter>Source Files\Other</Filter> + </ClCompile> + <ClCompile Include="message_table.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="file_version_info.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="version_info_viewer.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="version_info_editor.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="pe_resource_viewer.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="pe_properties_generic.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="pe_properties.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="pe_checksum.cpp"> + <Filter>Source Files\Other</Filter> + </ClCompile> + <ClCompile Include="pe_bound_import.cpp"> + <Filter>Source Files\PE Directories</Filter> + </ClCompile> + <ClCompile Include="resource_data_info.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="resource_bitmap_reader.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="resource_bitmap_writer.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="resource_cursor_icon_reader.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="resource_cursor_icon_writer.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="resource_version_info_reader.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="resource_version_info_writer.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="resource_string_table_reader.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="resource_message_list_reader.cpp"> + <Filter>Source Files\PE Resources</Filter> + </ClCompile> + <ClCompile Include="pe_rebuilder.cpp"> <Filter>Source Files</Filter> </ClCompile> </ItemGroup> <ItemGroup> - <ClInclude Include="pe_32_64.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="pe_base.h"> <Filter>Header Files</Filter> </ClInclude> @@ -44,15 +155,126 @@ <ClInclude Include="pe_factory.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="pe_structures.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="stdint_defs.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="pe_bliss.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="pe_section.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="pe_bound_import.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_debug.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_directory.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_dotnet.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_exception_directory.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_exports.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_imports.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_relocations.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_resources.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_tls.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="pe_load_config.h"> + <Filter>Header Files\PE Directories</Filter> + </ClInclude> + <ClInclude Include="entropy.h"> + <Filter>Header Files\Other</Filter> + </ClInclude> + <ClInclude Include="utils.h"> + <Filter>Header Files\Other</Filter> + </ClInclude> + <ClInclude Include="pe_rich_data.h"> + <Filter>Header Files\Other</Filter> + </ClInclude> <ClInclude Include="pe_resource_manager.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="message_table.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="file_version_info.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="version_info_viewer.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="pe_resource_viewer.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="version_info_editor.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="resource_internal.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="pe_properties.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="pe_structures.h"> + <ClInclude Include="pe_properties_generic.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="stdint_defs.h"> + <ClInclude Include="pe_checksum.h"> + <Filter>Header Files\Other</Filter> + </ClInclude> + <ClInclude Include="pe_bliss_resources.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="resource_data_info.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="resource_bitmap_reader.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="resource_bitmap_writer.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="resource_cursor_icon_reader.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="resource_cursor_icon_writer.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="resource_version_info_reader.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="resource_version_info_writer.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="resource_string_table_reader.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="resource_message_list_reader.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> + <ClInclude Include="pe_rebuilder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="version_info_types.h"> + <Filter>Header Files\PE Resources</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <None Include="readme.txt" /> diff --git a/pe_lib/pe_load_config.cpp b/pe_lib/pe_load_config.cpp new file mode 100644 index 0000000..451a15a --- /dev/null +++ b/pe_lib/pe_load_config.cpp @@ -0,0 +1,536 @@ +#include <algorithm> +#include <string.h> +#include "pe_load_config.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//IMAGE CONFIG +//Default constructor +image_config_info::image_config_info() + :time_stamp_(0), + major_version_(0), minor_version_(0), + global_flags_clear_(0), global_flags_set_(0), + critical_section_default_timeout_(0), + decommit_free_block_threshold_(0), decommit_total_free_threshold_(0), + lock_prefix_table_va_(0), + max_allocation_size_(0), + virtual_memory_threshold_(0), + process_affinity_mask_(0), + process_heap_flags_(0), + service_pack_version_(0), + edit_list_va_(0), + security_cookie_va_(0), + se_handler_table_va_(0), + se_handler_count_(0) +{} + +//Constructors from PE structures +template<typename ConfigStructure> +image_config_info::image_config_info(const ConfigStructure& info) + :time_stamp_(info.TimeDateStamp), + major_version_(info.MajorVersion), minor_version_(info.MinorVersion), + global_flags_clear_(info.GlobalFlagsClear), global_flags_set_(info.GlobalFlagsSet), + critical_section_default_timeout_(info.CriticalSectionDefaultTimeout), + decommit_free_block_threshold_(info.DeCommitFreeBlockThreshold), decommit_total_free_threshold_(info.DeCommitTotalFreeThreshold), + lock_prefix_table_va_(info.LockPrefixTable), + max_allocation_size_(info.MaximumAllocationSize), + virtual_memory_threshold_(info.VirtualMemoryThreshold), + process_affinity_mask_(info.ProcessAffinityMask), + process_heap_flags_(info.ProcessHeapFlags), + service_pack_version_(info.CSDVersion), + edit_list_va_(info.EditList), + security_cookie_va_(info.SecurityCookie), + se_handler_table_va_(info.SEHandlerTable), + se_handler_count_(info.SEHandlerCount) +{} + +//Instantiate template constructor with needed structures +template image_config_info::image_config_info(const image_load_config_directory32& info); +template image_config_info::image_config_info(const image_load_config_directory64& info); + +//Returns the date and time stamp value +uint32_t image_config_info::get_time_stamp() const +{ + return time_stamp_; +} + +//Returns major version number +uint16_t image_config_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version number +uint16_t image_config_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns clear global flags +uint32_t image_config_info::get_global_flags_clear() const +{ + return global_flags_clear_; +} + +//Returns set global flags +uint32_t image_config_info::get_global_flags_set() const +{ + return global_flags_set_; +} + +//Returns critical section default timeout +uint32_t image_config_info::get_critical_section_default_timeout() const +{ + return critical_section_default_timeout_; +} + +//Get the size of the minimum block that +//must be freed before it is freed (de-committed), in bytes +uint64_t image_config_info::get_decommit_free_block_threshold() const +{ + return decommit_free_block_threshold_; +} + +//Returns the size of the minimum total memory +//that must be freed in the process heap before it is freed (de-committed), in bytes +uint64_t image_config_info::get_decommit_total_free_threshold() const +{ + return decommit_total_free_threshold_; +} + +//Returns VA of a list of addresses where the LOCK prefix is used +uint64_t image_config_info::get_lock_prefix_table_va() const +{ + return lock_prefix_table_va_; +} + +//Returns the maximum allocation size, in bytes +uint64_t image_config_info::get_max_allocation_size() const +{ + return max_allocation_size_; +} + +//Returns the maximum block size that can be allocated from heap segments, in bytes +uint64_t image_config_info::get_virtual_memory_threshold() const +{ + return virtual_memory_threshold_; +} + +//Returns process affinity mask +uint64_t image_config_info::get_process_affinity_mask() const +{ + return process_affinity_mask_; +} + +//Returns process heap flags +uint32_t image_config_info::get_process_heap_flags() const +{ + return process_heap_flags_; +} + +//Returns service pack version (CSDVersion) +uint16_t image_config_info::get_service_pack_version() const +{ + return service_pack_version_; +} + +//Returns VA of edit list (reserved by system) +uint64_t image_config_info::get_edit_list_va() const +{ + return edit_list_va_; +} + +//Returns a pointer to a cookie that is used by Visual C++ or GS implementation +uint64_t image_config_info::get_security_cookie_va() const +{ + return security_cookie_va_; +} + +//Returns VA of the sorted table of RVAs of each valid, unique handler in the image +uint64_t image_config_info::get_se_handler_table_va() const +{ + return se_handler_table_va_; +} + +//Returns the count of unique handlers in the table +uint64_t image_config_info::get_se_handler_count() const +{ + return se_handler_count_; +} + +//Returns SE Handler RVA list +const image_config_info::se_handler_list& image_config_info::get_se_handler_rvas() const +{ + return se_handlers_; +} + +//Returns Lock Prefix RVA list +const image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas() const +{ + return lock_prefixes_; +} + +//Adds SE Handler RVA to list +void image_config_info::add_se_handler_rva(uint32_t rva) +{ + se_handlers_.push_back(rva); +} + +//Clears SE Handler list +void image_config_info::clear_se_handler_list() +{ + se_handlers_.clear(); +} + +//Adds Lock Prefix RVA to list +void image_config_info::add_lock_prefix_rva(uint32_t rva) +{ + lock_prefixes_.push_back(rva); +} + +//Clears Lock Prefix list +void image_config_info::clear_lock_prefix_list() +{ + lock_prefixes_.clear(); +} + +//Sets the date and time stamp value +void image_config_info::set_time_stamp(uint32_t time_stamp) +{ + time_stamp_ = time_stamp; +} + +//Sets major version number +void image_config_info::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version number +void image_config_info::set_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Sets clear global flags +void image_config_info::set_global_flags_clear(uint32_t global_flags_clear) +{ + global_flags_clear_ = global_flags_clear; +} + +//Sets set global flags +void image_config_info::set_global_flags_set(uint32_t global_flags_set) +{ + global_flags_set_ = global_flags_set; +} + +//Sets critical section default timeout +void image_config_info::set_critical_section_default_timeout(uint32_t critical_section_default_timeout) +{ + critical_section_default_timeout_ = critical_section_default_timeout; +} + +//Sets the size of the minimum block that +//must be freed before it is freed (de-committed), in bytes +void image_config_info::set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold) +{ + decommit_free_block_threshold_ = decommit_free_block_threshold; +} + +//Sets the size of the minimum total memory +//that must be freed in the process heap before it is freed (de-committed), in bytes +void image_config_info::set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold) +{ + decommit_total_free_threshold_ = decommit_total_free_threshold; +} + +//Sets VA of a list of addresses where the LOCK prefix is used +//If you rebuild this list, VA will be re-assigned automatically +void image_config_info::set_lock_prefix_table_va(uint64_t lock_prefix_table_va) +{ + lock_prefix_table_va_ = lock_prefix_table_va; +} + +//Sets the maximum allocation size, in bytes +void image_config_info::set_max_allocation_size(uint64_t max_allocation_size) +{ + max_allocation_size_ = max_allocation_size; +} + +//Sets the maximum block size that can be allocated from heap segments, in bytes +void image_config_info::set_virtual_memory_threshold(uint64_t virtual_memory_threshold) +{ + virtual_memory_threshold_ = virtual_memory_threshold; +} + +//Sets process affinity mask +void image_config_info::set_process_affinity_mask(uint64_t process_affinity_mask) +{ + process_affinity_mask_ = process_affinity_mask; +} + +//Sets process heap flags +void image_config_info::set_process_heap_flags(uint32_t process_heap_flags) +{ + process_heap_flags_ = process_heap_flags; +} + +//Sets service pack version (CSDVersion) +void image_config_info::set_service_pack_version(uint16_t service_pack_version) +{ + service_pack_version_ = service_pack_version; +} + +//Sets VA of edit list (reserved by system) +void image_config_info::set_edit_list_va(uint64_t edit_list_va) +{ + edit_list_va_ = edit_list_va; +} + +//Sets a pointer to a cookie that is used by Visual C++ or GS implementation +void image_config_info::set_security_cookie_va(uint64_t security_cookie_va) +{ + security_cookie_va_ = security_cookie_va; +} + +//Sets VA of the sorted table of RVAs of each valid, unique handler in the image +//If you rebuild this list, VA will be re-assigned automatically +void image_config_info::set_se_handler_table_va(uint64_t se_handler_table_va) +{ + se_handler_table_va_ = se_handler_table_va; +} + +//Returns SE Handler RVA list +image_config_info::se_handler_list& image_config_info::get_se_handler_rvas() +{ + return se_handlers_; +} + +//Returns Lock Prefix RVA list +image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas() +{ + return lock_prefixes_; +} + +//Returns image config info +//If image does not have config info, throws an exception +const image_config_info get_image_config(const pe_base& pe) +{ + return pe.get_pe_type() == pe_type_32 + ? get_image_config_base<pe_types_class_32>(pe) + : get_image_config_base<pe_types_class_64>(pe); +} + +//Image config rebuilder +const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) +{ + return pe.get_pe_type() == pe_type_32 + ? rebuild_image_config_base<pe_types_class_32>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section) + : rebuild_image_config_base<pe_types_class_64>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section); +} + + +//Returns image config info +//If image does not have config info, throws an exception +template<typename PEClassType> +const image_config_info get_image_config_base(const pe_base& pe) +{ + //Check if image has config directory + if(!pe.has_config()) + throw pe_exception("Image does not have load config directory", pe_exception::directory_does_not_exist); + + //Get load config structure + typename PEClassType::ConfigStruct config_info = pe.section_data_from_rva<typename PEClassType::ConfigStruct>(pe.get_directory_rva(image_directory_entry_load_config), section_data_virtual); + + //Check size of config directory + if(config_info.Size != sizeof(config_info)) + throw pe_exception("Incorrect (or old) load config directory", pe_exception::incorrect_config_directory); + + //Fill return structure + image_config_info ret(config_info); + + //Check possible overflow + if(config_info.SEHandlerCount >= pe_utils::max_dword / sizeof(uint32_t) + || config_info.SEHandlerTable >= static_cast<typename PEClassType::BaseSize>(-1) - config_info.SEHandlerCount * sizeof(uint32_t)) + throw pe_exception("Incorrect load config directory", pe_exception::incorrect_config_directory); + + //Read sorted SE handler RVA list (if any) + for(typename PEClassType::BaseSize i = 0; i != config_info.SEHandlerCount; ++i) + ret.add_se_handler_rva(pe.section_data_from_va<uint32_t>(static_cast<typename PEClassType::BaseSize>(config_info.SEHandlerTable + i * sizeof(uint32_t)))); + + if(config_info.LockPrefixTable) + { + //Read Lock Prefix VA list (if any) + unsigned long current = 0; + while(true) + { + typename PEClassType::BaseSize lock_prefix_va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(config_info.LockPrefixTable + current * sizeof(typename PEClassType::BaseSize))); + if(!lock_prefix_va) + break; + + ret.add_lock_prefix_rva(pe.va_to_rva(lock_prefix_va)); + + ++current; + } + } + + return ret; +} + +//Image config directory rebuilder +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure +//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure +template<typename PEClassType> +const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that image_config_section is attached to this PE image + if(!pe.section_attached(image_config_section)) + throw pe_exception("Image Config section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t alignment = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)) - offset_from_section_start; + + uint32_t needed_size = sizeof(typename PEClassType::ConfigStruct); //Calculate needed size for Image Config table + + uint32_t image_config_data_pos = offset_from_section_start + alignment; + + uint32_t current_pos_of_se_handlers = 0; + uint32_t current_pos_of_lock_prefixes = 0; + + if(write_se_handlers) + { + current_pos_of_se_handlers = needed_size + image_config_data_pos; + needed_size += static_cast<uint32_t>(info.get_se_handler_rvas().size()) * sizeof(uint32_t); //RVAs of SE Handlers + } + + if(write_lock_prefixes) + { + current_pos_of_lock_prefixes = needed_size + image_config_data_pos; + needed_size += static_cast<uint32_t>((info.get_lock_prefix_rvas().size() + 1) * sizeof(typename PEClassType::BaseSize)); //VAs of Lock Prefixes (and ending null element) + } + + //Check if image_config_section is last one. If it's not, check if there's enough place for Image Config data + if(&image_config_section != &*(pe.get_image_sections().end() - 1) && + (image_config_section.empty() || pe_utils::align_up(image_config_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + image_config_data_pos)) + throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space); + + std::string& raw_data = image_config_section.get_raw_data(); + + //This will be done only if image_config_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + image_config_data_pos) + raw_data.resize(needed_size + image_config_data_pos); //Expand section raw data + + //Create and fill Image Config structure + typename PEClassType::ConfigStruct image_config_section_struct = {0}; + image_config_section_struct.Size = sizeof(image_config_section_struct); + image_config_section_struct.TimeDateStamp = info.get_time_stamp(); + image_config_section_struct.MajorVersion = info.get_major_version(); + image_config_section_struct.MinorVersion = info.get_minor_version(); + image_config_section_struct.GlobalFlagsClear = info.get_global_flags_clear(); + image_config_section_struct.GlobalFlagsSet = info.get_global_flags_set(); + image_config_section_struct.CriticalSectionDefaultTimeout = info.get_critical_section_default_timeout(); + image_config_section_struct.DeCommitFreeBlockThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_free_block_threshold()); + image_config_section_struct.DeCommitTotalFreeThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_total_free_threshold()); + image_config_section_struct.MaximumAllocationSize = static_cast<typename PEClassType::BaseSize>(info.get_max_allocation_size()); + image_config_section_struct.VirtualMemoryThreshold = static_cast<typename PEClassType::BaseSize>(info.get_virtual_memory_threshold()); + image_config_section_struct.ProcessHeapFlags = info.get_process_heap_flags(); + image_config_section_struct.ProcessAffinityMask = static_cast<typename PEClassType::BaseSize>(info.get_process_affinity_mask()); + image_config_section_struct.CSDVersion = info.get_service_pack_version(); + image_config_section_struct.EditList = static_cast<typename PEClassType::BaseSize>(info.get_edit_list_va()); + image_config_section_struct.SecurityCookie = static_cast<typename PEClassType::BaseSize>(info.get_security_cookie_va()); + image_config_section_struct.SEHandlerCount = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_rvas().size()); + + + if(write_se_handlers) + { + if(info.get_se_handler_rvas().empty()) + { + write_se_handlers = false; + image_config_section_struct.SEHandlerTable = 0; + } + else + { + typename PEClassType::BaseSize va; + pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_se_handlers), va); + image_config_section_struct.SEHandlerTable = va; + } + } + else + { + image_config_section_struct.SEHandlerTable = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_table_va()); + } + + if(write_lock_prefixes) + { + if(info.get_lock_prefix_rvas().empty()) + { + write_lock_prefixes = false; + image_config_section_struct.LockPrefixTable = 0; + } + else + { + typename PEClassType::BaseSize va; + pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_lock_prefixes), va); + image_config_section_struct.LockPrefixTable = va; + } + } + else + { + image_config_section_struct.LockPrefixTable = static_cast<typename PEClassType::BaseSize>(info.get_lock_prefix_table_va()); + } + + //Write image config section + memcpy(&raw_data[image_config_data_pos], &image_config_section_struct, sizeof(image_config_section_struct)); + + if(write_se_handlers) + { + //Sort SE Handlers list + image_config_info::se_handler_list sorted_list = info.get_se_handler_rvas(); + std::sort(sorted_list.begin(), sorted_list.end()); + + //Write SE Handlers table + for(image_config_info::se_handler_list::const_iterator it = sorted_list.begin(); it != sorted_list.end(); ++it) + { + uint32_t se_handler_rva = *it; + memcpy(&raw_data[current_pos_of_se_handlers], &se_handler_rva, sizeof(se_handler_rva)); + current_pos_of_se_handlers += sizeof(se_handler_rva); + } + } + + if(write_lock_prefixes) + { + //Write Lock Prefixes VA list + for(image_config_info::lock_prefix_rva_list::const_iterator it = info.get_lock_prefix_rvas().begin(); it != info.get_lock_prefix_rvas().end(); ++it) + { + typename PEClassType::BaseSize lock_prefix_va; + pe.rva_to_va(*it, lock_prefix_va); + memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); + current_pos_of_lock_prefixes += sizeof(lock_prefix_va); + } + + { + //Ending null VA + typename PEClassType::BaseSize lock_prefix_va = 0; + memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); + } + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(image_config_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(image_config_section, image_config_data_pos), sizeof(typename PEClassType::ConfigStruct)); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_load_config, ret.get_rva()); + pe.set_directory_size(image_directory_entry_load_config, ret.get_size()); + } + + return ret; +} + +} diff --git a/pe_lib/pe_load_config.h b/pe_lib/pe_load_config.h new file mode 100644 index 0000000..059e215 --- /dev/null +++ b/pe_lib/pe_load_config.h @@ -0,0 +1,163 @@ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing image configuration information +class image_config_info +{ +public: + typedef std::vector<uint32_t> se_handler_list; + typedef std::vector<uint32_t> lock_prefix_rva_list; + +public: + //Default constructor + image_config_info(); + //Constructors from PE structures (no checks) + template<typename ConfigStructure> + explicit image_config_info(const ConfigStructure& info); + + //Returns the date and time stamp value + uint32_t get_time_stamp() const; + //Returns major version number + uint16_t get_major_version() const; + //Returns minor version number + uint16_t get_minor_version() const; + //Returns clear global flags + uint32_t get_global_flags_clear() const; + //Returns set global flags + uint32_t get_global_flags_set() const; + //Returns critical section default timeout + uint32_t get_critical_section_default_timeout() const; + //Get the size of the minimum block that + //must be freed before it is freed (de-committed), in bytes + uint64_t get_decommit_free_block_threshold() const; + //Returns the size of the minimum total memory + //that must be freed in the process heap before it is freed (de-committed), in bytes + uint64_t get_decommit_total_free_threshold() const; + //Returns VA of a list of addresses where the LOCK prefix is used + uint64_t get_lock_prefix_table_va() const; + //Returns the maximum allocation size, in bytes + uint64_t get_max_allocation_size() const; + //Returns the maximum block size that can be allocated from heap segments, in bytes + uint64_t get_virtual_memory_threshold() const; + //Returns process affinity mask + uint64_t get_process_affinity_mask() const; + //Returns process heap flags + uint32_t get_process_heap_flags() const; + //Returns service pack version (CSDVersion) + uint16_t get_service_pack_version() const; + //Returns VA of edit list (reserved by system) + uint64_t get_edit_list_va() const; + //Returns a pointer to a cookie that is used by Visual C++ or GS implementation + uint64_t get_security_cookie_va() const; + //Returns VA of the sorted table of RVAs of each valid, unique handler in the image + uint64_t get_se_handler_table_va() const; + //Returns the count of unique handlers in the table + uint64_t get_se_handler_count() const; + + //Returns SE Handler RVA list + const se_handler_list& get_se_handler_rvas() const; + + //Returns Lock Prefix RVA list + const lock_prefix_rva_list& get_lock_prefix_rvas() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Also you can use these functions to rebuild image config directory + + //Adds SE Handler RVA to list + void add_se_handler_rva(uint32_t rva); + //Clears SE Handler list + void clear_se_handler_list(); + + //Adds Lock Prefix RVA to list + void add_lock_prefix_rva(uint32_t rva); + //Clears Lock Prefix list + void clear_lock_prefix_list(); + + //Sets the date and time stamp value + void set_time_stamp(uint32_t time_stamp); + //Sets major version number + void set_major_version(uint16_t major_version); + //Sets minor version number + void set_minor_version(uint16_t minor_version); + //Sets clear global flags + void set_global_flags_clear(uint32_t global_flags_clear); + //Sets set global flags + void set_global_flags_set(uint32_t global_flags_set); + //Sets critical section default timeout + void set_critical_section_default_timeout(uint32_t critical_section_default_timeout); + //Sets the size of the minimum block that + //must be freed before it is freed (de-committed), in bytes + void set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold); + //Sets the size of the minimum total memory + //that must be freed in the process heap before it is freed (de-committed), in bytes + void set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold); + //Sets VA of a list of addresses where the LOCK prefix is used + //If you rebuild this list, VA will be re-assigned automatically + void set_lock_prefix_table_va(uint64_t lock_prefix_table_va); + //Sets the maximum allocation size, in bytes + void set_max_allocation_size(uint64_t max_allocation_size); + //Sets the maximum block size that can be allocated from heap segments, in bytes + void set_virtual_memory_threshold(uint64_t virtual_memory_threshold); + //Sets process affinity mask + void set_process_affinity_mask(uint64_t process_affinity_mask); + //Sets process heap flags + void set_process_heap_flags(uint32_t process_heap_flags); + //Sets service pack version (CSDVersion) + void set_service_pack_version(uint16_t service_pack_version); + //Sets VA of edit list (reserved by system) + void set_edit_list_va(uint64_t edit_list_va); + //Sets a pointer to a cookie that is used by Visual C++ or GS implementation + void set_security_cookie_va(uint64_t security_cookie_va); + //Sets VA of the sorted table of RVAs of each valid, unique handler in the image + //If you rebuild this list, VA will be re-assigned automatically + void set_se_handler_table_va(uint64_t se_handler_table_va); + + //Returns SE Handler RVA list + se_handler_list& get_se_handler_rvas(); + + //Returns Lock Prefix RVA list + lock_prefix_rva_list& get_lock_prefix_rvas(); + +private: + uint32_t time_stamp_; + uint16_t major_version_, minor_version_; + uint32_t global_flags_clear_, global_flags_set_; + uint32_t critical_section_default_timeout_; + uint64_t decommit_free_block_threshold_, decommit_total_free_threshold_; + uint64_t lock_prefix_table_va_; + uint64_t max_allocation_size_; + uint64_t virtual_memory_threshold_; + uint64_t process_affinity_mask_; + uint32_t process_heap_flags_; + uint16_t service_pack_version_; + uint64_t edit_list_va_; + uint64_t security_cookie_va_; + uint64_t se_handler_table_va_; + uint64_t se_handler_count_; + + se_handler_list se_handlers_; + lock_prefix_rva_list lock_prefixes_; +}; + +//Returns image config info +//If image does not have config info, throws an exception +const image_config_info get_image_config(const pe_base& pe); + +template<typename PEClassType> +const image_config_info get_image_config_base(const pe_base& pe); + + +//Image config directory rebuilder +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure +//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure +const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +template<typename PEClassType> +const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/pe_lib/pe_properties.cpp b/pe_lib/pe_properties.cpp new file mode 100644 index 0000000..94ed949 --- /dev/null +++ b/pe_lib/pe_properties.cpp @@ -0,0 +1,20 @@ +#include "pe_properties.h" + +namespace pe_bliss +{ +//Destructor +pe_properties::~pe_properties() +{} + +//Clears PE characteristics flag +void pe_properties::clear_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() & ~flags); +} + +//Sets PE characteristics flag +void pe_properties::set_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() | flags); +} +} diff --git a/pe_lib/pe_properties.h b/pe_lib/pe_properties.h new file mode 100644 index 0000000..641a78d --- /dev/null +++ b/pe_lib/pe_properties.h @@ -0,0 +1,215 @@ +#pragma once +#include <memory> +#include "pe_structures.h" + +namespace pe_bliss +{ +class pe_properties +{ +public: //Constructors + virtual std::auto_ptr<pe_properties> duplicate() const = 0; + + //Fills properly PE structures + virtual void create_pe(uint32_t section_alignment, uint16_t subsystem) = 0; + +public: + //Destructor + virtual ~pe_properties(); + + +public: //DIRECTORIES + //Returns true if directory exists + virtual bool directory_exists(uint32_t id) const = 0; + + //Removes directory + virtual void remove_directory(uint32_t id) = 0; + + //Returns directory RVA + virtual uint32_t get_directory_rva(uint32_t id) const = 0; + //Returns directory size + virtual uint32_t get_directory_size(uint32_t id) const = 0; + + //Sets directory RVA (just a value of PE header, no moving occurs) + virtual void set_directory_rva(uint32_t id, uint32_t rva) = 0; + //Sets directory size (just a value of PE header, no moving occurs) + virtual void set_directory_size(uint32_t id, uint32_t size) = 0; + + //Strips only zero DATA_DIRECTORY entries to count = min_count + //Returns resulting number of data directories + //strip_iat_directory - if true, even not empty IAT directory will be stripped + virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true) = 0; + + +public: //IMAGE + //Returns PE type of this image + virtual pe_type get_pe_type() const = 0; + + +public: //PE HEADER + //Returns image base for PE32 and PE64 respectively + virtual uint32_t get_image_base_32() const = 0; + virtual uint64_t get_image_base_64() const = 0; + + //Sets new image base for PE32 + virtual void set_image_base(uint32_t base) = 0; + //Sets new image base for PE32/PE+ + virtual void set_image_base_64(uint64_t base) = 0; + + //Returns image entry point + virtual uint32_t get_ep() const = 0; + //Sets image entry point + virtual void set_ep(uint32_t new_ep) = 0; + + //Returns file alignment + virtual uint32_t get_file_alignment() const = 0; + //Returns section alignment + virtual uint32_t get_section_alignment() const = 0; + + //Sets heap size commit for PE32 and PE64 respectively + virtual void set_heap_size_commit(uint32_t size) = 0; + virtual void set_heap_size_commit(uint64_t size) = 0; + //Sets heap size reserve for PE32 and PE64 respectively + virtual void set_heap_size_reserve(uint32_t size) = 0; + virtual void set_heap_size_reserve(uint64_t size) = 0; + //Sets stack size commit for PE32 and PE64 respectively + virtual void set_stack_size_commit(uint32_t size) = 0; + virtual void set_stack_size_commit(uint64_t size) = 0; + //Sets stack size reserve for PE32 and PE64 respectively + virtual void set_stack_size_reserve(uint32_t size) = 0; + virtual void set_stack_size_reserve(uint64_t size) = 0; + + //Returns heap size commit for PE32 and PE64 respectively + virtual uint32_t get_heap_size_commit_32() const = 0; + virtual uint64_t get_heap_size_commit_64() const = 0; + //Returns heap size reserve for PE32 and PE64 respectively + virtual uint32_t get_heap_size_reserve_32() const = 0; + virtual uint64_t get_heap_size_reserve_64() const = 0; + //Returns stack size commit for PE32 and PE64 respectively + virtual uint32_t get_stack_size_commit_32() const = 0; + virtual uint64_t get_stack_size_commit_64() const = 0; + //Returns stack size reserve for PE32 and PE64 respectively + virtual uint32_t get_stack_size_reserve_32() const = 0; + virtual uint64_t get_stack_size_reserve_64() const = 0; + + //Returns virtual size of image + virtual uint32_t get_size_of_image() const = 0; + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual uint32_t get_number_of_rvas_and_sizes() const = 0; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual void set_number_of_rvas_and_sizes(uint32_t number) = 0; + + //Returns PE characteristics + virtual uint16_t get_characteristics() const = 0; + //Sets PE characteristics + virtual void set_characteristics(uint16_t ch) = 0; + + //Clears PE characteristics flag + void clear_characteristics_flags(uint16_t flags); + //Sets PE characteristics flag + void set_characteristics_flags(uint16_t flags); + + //Returns size of headers + virtual uint32_t get_size_of_headers() const = 0; + + //Returns subsystem + virtual uint16_t get_subsystem() const = 0; + + //Sets subsystem + virtual void set_subsystem(uint16_t subsystem) = 0; + + //Returns size of optional header + virtual uint16_t get_size_of_optional_header() const = 0; + + //Returns PE signature + virtual uint32_t get_pe_signature() const = 0; + + //Returns PE magic value + virtual uint32_t get_magic() const = 0; + + //Returns checksum of PE file from header + virtual uint32_t get_checksum() const = 0; + + //Sets checksum of PE file + virtual void set_checksum(uint32_t checksum) = 0; + + //Returns timestamp of PE file from header + virtual uint32_t get_time_date_stamp() const = 0; + + //Sets timestamp of PE file + virtual void set_time_date_stamp(uint32_t timestamp) = 0; + + //Returns Machine field value of PE file from header + virtual uint16_t get_machine() const = 0; + + //Sets Machine field value of PE file + virtual void set_machine(uint16_t machine) = 0; + + //Returns DLL Characteristics + virtual uint16_t get_dll_characteristics() const = 0; + + //Sets DLL Characteristics + virtual void set_dll_characteristics(uint16_t characteristics) = 0; + + //Sets required operation system version + virtual void set_os_version(uint16_t major, uint16_t minor) = 0; + + //Returns required operation system version (minor word) + virtual uint16_t get_minor_os_version() const = 0; + + //Returns required operation system version (major word) + virtual uint16_t get_major_os_version() const = 0; + + //Sets required subsystem version + virtual void set_subsystem_version(uint16_t major, uint16_t minor) = 0; + + //Returns required subsystem version (minor word) + virtual uint16_t get_minor_subsystem_version() const = 0; + + //Returns required subsystem version (major word) + virtual uint16_t get_major_subsystem_version() const = 0; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const = 0; + virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const = 0; + + //Relative Virtual Address (RVA) to Virtual Address (VA) convertions + //for PE32 and PE64 respectively + virtual uint32_t rva_to_va_32(uint32_t rva) const = 0; + virtual uint64_t rva_to_va_64(uint32_t rva) const = 0; + + +public: //SECTIONS + //Returns number of sections + virtual uint16_t get_number_of_sections() const = 0; + +public: + //Sets number of sections + virtual void set_number_of_sections(uint16_t number) = 0; + //Sets virtual size of image + virtual void set_size_of_image(uint32_t size) = 0; + //Sets size of headers + virtual void set_size_of_headers(uint32_t size) = 0; + //Sets size of optional headers + virtual void set_size_of_optional_header(uint16_t size) = 0; + //Returns nt headers data pointer + virtual char* get_nt_headers_ptr() = 0; + //Returns nt headers data pointer + virtual const char* get_nt_headers_ptr() const = 0; + //Returns size of NT header + virtual uint32_t get_sizeof_nt_header() const = 0; + //Returns size of optional headers + virtual uint32_t get_sizeof_opt_headers() const = 0; + //Sets file alignment (no checks) + virtual void set_file_alignment_unchecked(uint32_t alignment) = 0; + //Sets base of code + virtual void set_base_of_code(uint32_t base) = 0; + //Returns base of code + virtual uint32_t get_base_of_code() const = 0; + //Returns needed PE magic for PE or PE+ (from template parameters) + virtual uint32_t get_needed_magic() const = 0; +}; +} diff --git a/pe_lib/pe_properties_generic.cpp b/pe_lib/pe_properties_generic.cpp new file mode 100644 index 0000000..a783135 --- /dev/null +++ b/pe_lib/pe_properties_generic.cpp @@ -0,0 +1,624 @@ +#include <string.h> +#include "pe_properties_generic.h" +#include "pe_exception.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor +template<typename PEClassType> +std::auto_ptr<pe_properties> pe_properties_generic<PEClassType>::duplicate() const +{ + return std::auto_ptr<pe_properties>(new pe_properties_generic<PEClassType>(*this)); +} + +//Fills properly PE structures +template<typename PEClassType> +void pe_properties_generic<PEClassType>::create_pe(uint32_t section_alignment, uint16_t subsystem) +{ + memset(&nt_headers_, 0, sizeof(nt_headers_)); + nt_headers_.Signature = 0x4550; //"PE" + nt_headers_.FileHeader.Machine = 0x14C; //i386 + nt_headers_.FileHeader.SizeOfOptionalHeader = sizeof(nt_headers_.OptionalHeader); + nt_headers_.OptionalHeader.Magic = PEClassType::Id; + nt_headers_.OptionalHeader.ImageBase = 0x400000; + nt_headers_.OptionalHeader.SectionAlignment = section_alignment; + nt_headers_.OptionalHeader.FileAlignment = 0x200; + nt_headers_.OptionalHeader.SizeOfHeaders = 1024; + nt_headers_.OptionalHeader.Subsystem = subsystem; + nt_headers_.OptionalHeader.SizeOfHeapReserve = 0x100000; + nt_headers_.OptionalHeader.SizeOfHeapCommit = 0x1000; + nt_headers_.OptionalHeader.SizeOfStackReserve = 0x100000; + nt_headers_.OptionalHeader.SizeOfStackCommit = 0x1000; + nt_headers_.OptionalHeader.NumberOfRvaAndSizes = 0x10; +} + +//Duplicate +template<typename PEClassType> +pe_properties_generic<PEClassType>::~pe_properties_generic() +{} + +//Returns true if directory exists +template<typename PEClassType> +bool pe_properties_generic<PEClassType>::directory_exists(uint32_t id) const +{ + return (nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1) >= id && + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress; +} + +//Removes directory +template<typename PEClassType> +void pe_properties_generic<PEClassType>::remove_directory(uint32_t id) +{ + if(directory_exists(id)) + { + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = 0; + nt_headers_.OptionalHeader.DataDirectory[id].Size = 0; + + if(id == image_directory_entry_basereloc) + { + set_characteristics_flags(image_file_relocs_stripped); + set_dll_characteristics(get_dll_characteristics() & ~image_dllcharacteristics_dynamic_base); + } + else if(id == image_directory_entry_export) + { + clear_characteristics_flags(image_file_dll); + } + } +} + +//Returns directory RVA +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_directory_rva(uint32_t id) const +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + return nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress; +} + +//Returns directory size +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_directory_rva(uint32_t id, uint32_t va) +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = va; +} + +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_directory_size(uint32_t id, uint32_t size) +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + nt_headers_.OptionalHeader.DataDirectory[id].Size = size; +} + +//Returns directory size +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_directory_size(uint32_t id) const +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + return nt_headers_.OptionalHeader.DataDirectory[id].Size; +} + +//Strips only zero DATA_DIRECTORY entries to count = min_count +//Returns resulting number of data directories +//strip_iat_directory - if true, even not empty IAT directory will be stripped +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::strip_data_directories(uint32_t min_count, bool strip_iat_directory) +{ + int i = nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1; + + //Enumerate all data directories from the end + for(; i >= 0; i--) + { + //If directory exists, break + if(nt_headers_.OptionalHeader.DataDirectory[i].VirtualAddress && (static_cast<uint32_t>(i) != image_directory_entry_iat || !strip_iat_directory)) + break; + + if(i <= static_cast<int>(min_count) - 2) + break; + } + + if(i == image_numberof_directory_entries - 1) + return image_numberof_directory_entries; + + //Return new number of data directories + return nt_headers_.OptionalHeader.NumberOfRvaAndSizes = i + 1; +} + +//Returns image base for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_image_base_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase); +} + +//Returns image base for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_image_base_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.ImageBase); +} + +//Sets new image base +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_image_base(uint32_t base) +{ + nt_headers_.OptionalHeader.ImageBase = base; +} + +//Sets new image base +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_image_base_64(uint64_t base) +{ + nt_headers_.OptionalHeader.ImageBase = static_cast<typename PEClassType::BaseSize>(base); +} + +//Returns image entry point +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_ep() const +{ + return nt_headers_.OptionalHeader.AddressOfEntryPoint; +} + +//Sets image entry point +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_ep(uint32_t new_ep) +{ + nt_headers_.OptionalHeader.AddressOfEntryPoint = new_ep; +} + +//Returns file alignment +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_file_alignment() const +{ + return nt_headers_.OptionalHeader.FileAlignment; +} + +//Returns section alignment +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_section_alignment() const +{ + return nt_headers_.OptionalHeader.SectionAlignment; +} + +//Sets heap size commit for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_commit(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size commit for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_commit(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size reserve for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size reserve for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size commit for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_commit(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size commit for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_commit(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size reserve for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size reserve for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Returns heap size commit for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_heap_size_commit_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit); +} + +//Returns heap size commit for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_heap_size_commit_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit); +} + +//Returns heap size reserve for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_heap_size_reserve_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve); +} + +//Returns heap size reserve for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_heap_size_reserve_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve); +} + +//Returns stack size commit for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_stack_size_commit_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackCommit); +} + +//Returns stack size commit for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_stack_size_commit_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackCommit); +} + +//Returns stack size reserve for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_stack_size_reserve_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackReserve); +} + +//Returns stack size reserve for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_stack_size_reserve_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackReserve); +} + +//Returns virtual size of image +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_size_of_image() const +{ + return nt_headers_.OptionalHeader.SizeOfImage; +} + +//Returns number of RVA and sizes (number of DATA_DIRECTORY entries) +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_number_of_rvas_and_sizes() const +{ + return nt_headers_.OptionalHeader.NumberOfRvaAndSizes; +} + +//Sets number of RVA and sizes (number of DATA_DIRECTORY entries) +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_number_of_rvas_and_sizes(uint32_t number) +{ + nt_headers_.OptionalHeader.NumberOfRvaAndSizes = number; +} + +//Returns PE characteristics +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_characteristics() const +{ + return nt_headers_.FileHeader.Characteristics; +} + +//Returns checksum of PE file from header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_checksum() const +{ + return nt_headers_.OptionalHeader.CheckSum; +} + +//Sets checksum of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_checksum(uint32_t checksum) +{ + nt_headers_.OptionalHeader.CheckSum = checksum; +} + +//Returns DLL Characteristics +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_dll_characteristics() const +{ + return nt_headers_.OptionalHeader.DllCharacteristics; +} + +//Returns timestamp of PE file from header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_time_date_stamp() const +{ + return nt_headers_.FileHeader.TimeDateStamp; +} + +//Sets timestamp of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_time_date_stamp(uint32_t timestamp) +{ + nt_headers_.FileHeader.TimeDateStamp = timestamp; +} + +//Sets DLL Characteristics +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_dll_characteristics(uint16_t characteristics) +{ + nt_headers_.OptionalHeader.DllCharacteristics = characteristics; +} + +//Returns Machine field value of PE file from header +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_machine() const +{ + return nt_headers_.FileHeader.Machine; +} + +//Sets Machine field value of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_machine(uint16_t machine) +{ + nt_headers_.FileHeader.Machine = machine; +} + +//Sets PE characteristics +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_characteristics(uint16_t ch) +{ + nt_headers_.FileHeader.Characteristics = ch; +} + +//Returns size of headers +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_size_of_headers() const +{ + return nt_headers_.OptionalHeader.SizeOfHeaders; +} + +//Returns subsystem +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_subsystem() const +{ + return nt_headers_.OptionalHeader.Subsystem; +} + +//Sets subsystem +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_subsystem(uint16_t subsystem) +{ + nt_headers_.OptionalHeader.Subsystem = subsystem; +} + +//Returns size of optional header +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_size_of_optional_header() const +{ + return nt_headers_.FileHeader.SizeOfOptionalHeader; +} + +//Returns PE signature +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_pe_signature() const +{ + return nt_headers_.Signature; +} + +//Returns PE magic value +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_magic() const +{ + return nt_headers_.OptionalHeader.Magic; +} + +//Sets required operation system version +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_os_version(uint16_t major, uint16_t minor) +{ + nt_headers_.OptionalHeader.MinorOperatingSystemVersion = minor; + nt_headers_.OptionalHeader.MajorOperatingSystemVersion = major; +} + +//Returns required operation system version (minor word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_minor_os_version() const +{ + return nt_headers_.OptionalHeader.MinorOperatingSystemVersion; +} + +//Returns required operation system version (major word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_major_os_version() const +{ + return nt_headers_.OptionalHeader.MajorOperatingSystemVersion; +} + +//Sets required subsystem version +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_subsystem_version(uint16_t major, uint16_t minor) +{ + nt_headers_.OptionalHeader.MinorSubsystemVersion = minor; + nt_headers_.OptionalHeader.MajorSubsystemVersion = major; +} + +//Returns required subsystem version (minor word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_minor_subsystem_version() const +{ + return nt_headers_.OptionalHeader.MinorSubsystemVersion; +} + +//Returns required subsystem version (major word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_major_subsystem_version() const +{ + return nt_headers_.OptionalHeader.MajorSubsystemVersion; +} + +//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint32_t va, bool bound_check) const +{ + if(bound_check && static_cast<uint64_t>(va) - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase); +} + +//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32/PE64 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint64_t va, bool bound_check) const +{ + if(bound_check && va - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::rva_to_va_32(uint32_t rva) const +{ + if(!pe_utils::is_sum_safe(rva, static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase))) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(rva + nt_headers_.OptionalHeader.ImageBase); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::rva_to_va_64(uint32_t rva) const +{ + return static_cast<uint64_t>(rva) + nt_headers_.OptionalHeader.ImageBase; +} + +//Returns number of sections +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_number_of_sections() const +{ + return nt_headers_.FileHeader.NumberOfSections; +} + +//Sets number of sections +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_number_of_sections(uint16_t number) +{ + nt_headers_.FileHeader.NumberOfSections = number; +} + +//Sets virtual size of image +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_image(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfImage = size; +} + +//Sets size of headers +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_headers(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeaders = size; +} + +//Sets size of optional headers +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_optional_header(uint16_t size) +{ + nt_headers_.FileHeader.SizeOfOptionalHeader = size; +} + +//Returns nt headers data pointer +template<typename PEClassType> +char* pe_properties_generic<PEClassType>::get_nt_headers_ptr() +{ + return reinterpret_cast<char*>(&nt_headers_); +} + +//Returns nt headers data pointer +template<typename PEClassType> +const char* pe_properties_generic<PEClassType>::get_nt_headers_ptr() const +{ + return reinterpret_cast<const char*>(&nt_headers_); +} + +//Returns size of NT header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_sizeof_nt_header() const +{ + return sizeof(typename PEClassType::NtHeaders); +} + +//Returns size of optional headers +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_sizeof_opt_headers() const +{ + return sizeof(typename PEClassType::OptHeaders); +} + +//Sets file alignment (no checks) +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_file_alignment_unchecked(uint32_t alignment) +{ + nt_headers_.OptionalHeader.FileAlignment = alignment; +} + +//Sets base of code +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_base_of_code(uint32_t base) +{ + nt_headers_.OptionalHeader.BaseOfCode = base; +} + +//Returns base of code +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_base_of_code() const +{ + return nt_headers_.OptionalHeader.BaseOfCode; +} + +//Returns needed PE magic for PE or PE+ (from template parameters) +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_needed_magic() const +{ + return PEClassType::Id; +} + +//Returns PE type of this image +template<typename PEClassType> +pe_type pe_properties_generic<PEClassType>::get_pe_type() const +{ + return PEClassType::Id == image_nt_optional_hdr32_magic ? pe_type_32 : pe_type_64; +} + +//Two used instantiations for PE32 (PE) and PE64 (PE+) +template class pe_properties_generic<pe_types_class_32>; +template class pe_properties_generic<pe_types_class_64>; +} diff --git a/pe_lib/pe_properties_generic.h b/pe_lib/pe_properties_generic.h new file mode 100644 index 0000000..a804c4b --- /dev/null +++ b/pe_lib/pe_properties_generic.h @@ -0,0 +1,256 @@ +#include "pe_properties.h" + +namespace pe_bliss +{ +//Helper class to reduce code size and ease its editing +template< + typename NtHeadersType, + typename OptHeadersType, + uint16_t IdVal, + typename BaseSizeType, + BaseSizeType ImportSnapFlagVal, + typename TLSStructType, + typename ConfigStructType> +class pe_types +{ +public: + typedef NtHeadersType NtHeaders; //NT HEADERS type + typedef OptHeadersType OptHeaders; //NT OPTIONAL HEADER type + typedef BaseSizeType BaseSize; //Base size of different values: DWORD or ULONGLONG + typedef TLSStructType TLSStruct; //TLS structure type + typedef ConfigStructType ConfigStruct; //Configuration structure type + + static const uint16_t Id = IdVal; //Magic of PE or PE+ + static const BaseSize ImportSnapFlag = ImportSnapFlagVal; //Import snap flag value +}; + +//Portable Executable derived class for PE and PE+ +//Describes PE/PE+ dependent things +template<typename PEClassType> +class pe_properties_generic : public pe_properties +{ +public: //Constructor + virtual std::auto_ptr<pe_properties> duplicate() const; + + //Fills properly PE structures + virtual void create_pe(uint32_t section_alignment, uint16_t subsystem); + +public: + //Destructor + virtual ~pe_properties_generic(); + + +public: //DIRECTORIES + //Returns true if directory exists + virtual bool directory_exists(uint32_t id) const; + + //Removes directory + virtual void remove_directory(uint32_t id); + + //Returns directory RVA + virtual uint32_t get_directory_rva(uint32_t id) const; + //Returns directory size + virtual uint32_t get_directory_size(uint32_t id) const; + + //Sets directory RVA (just a value of PE header, no moving occurs) + virtual void set_directory_rva(uint32_t id, uint32_t rva); + //Sets directory size (just a value of PE header, no moving occurs) + virtual void set_directory_size(uint32_t id, uint32_t size); + + //Strips only zero DATA_DIRECTORY entries to count = min_count + //Returns resulting number of data directories + //strip_iat_directory - if true, even not empty IAT directory will be stripped + virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true); + + +public: //IMAGE + //Returns PE type of this image + virtual pe_type get_pe_type() const; + + +public: //PE HEADER + //Returns image base for PE32 and PE64 respectively + virtual uint32_t get_image_base_32() const; + virtual uint64_t get_image_base_64() const; + + //Sets new image base for PE32 + virtual void set_image_base(uint32_t base); + //Sets new image base for PE32/PE+ + virtual void set_image_base_64(uint64_t base); + + //Returns image entry point + virtual uint32_t get_ep() const; + //Sets image entry point + virtual void set_ep(uint32_t new_ep); + + //Returns file alignment + virtual uint32_t get_file_alignment() const; + //Returns section alignment + virtual uint32_t get_section_alignment() const; + + //Sets heap size commit for PE32 and PE64 respectively + virtual void set_heap_size_commit(uint32_t size); + virtual void set_heap_size_commit(uint64_t size); + //Sets heap size reserve for PE32 and PE64 respectively + virtual void set_heap_size_reserve(uint32_t size); + virtual void set_heap_size_reserve(uint64_t size); + //Sets stack size commit for PE32 and PE64 respectively + virtual void set_stack_size_commit(uint32_t size); + virtual void set_stack_size_commit(uint64_t size); + //Sets stack size reserve for PE32 and PE64 respectively + virtual void set_stack_size_reserve(uint32_t size); + virtual void set_stack_size_reserve(uint64_t size); + + //Returns heap size commit for PE32 and PE64 respectively + virtual uint32_t get_heap_size_commit_32() const; + virtual uint64_t get_heap_size_commit_64() const; + //Returns heap size reserve for PE32 and PE64 respectively + virtual uint32_t get_heap_size_reserve_32() const; + virtual uint64_t get_heap_size_reserve_64() const; + //Returns stack size commit for PE32 and PE64 respectively + virtual uint32_t get_stack_size_commit_32() const; + virtual uint64_t get_stack_size_commit_64() const; + //Returns stack size reserve for PE32 and PE64 respectively + virtual uint32_t get_stack_size_reserve_32() const; + virtual uint64_t get_stack_size_reserve_64() const; + + //Returns virtual size of image + virtual uint32_t get_size_of_image() const; + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual uint32_t get_number_of_rvas_and_sizes() const; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual void set_number_of_rvas_and_sizes(uint32_t number); + + //Returns PE characteristics + virtual uint16_t get_characteristics() const; + //Sets PE characteristics + virtual void set_characteristics(uint16_t ch); + + //Returns size of headers + virtual uint32_t get_size_of_headers() const; + + //Returns subsystem + virtual uint16_t get_subsystem() const; + + //Sets subsystem + virtual void set_subsystem(uint16_t subsystem); + + //Returns size of optional header + virtual uint16_t get_size_of_optional_header() const; + + //Returns PE signature + virtual uint32_t get_pe_signature() const; + + //Returns PE magic value + virtual uint32_t get_magic() const; + + //Returns checksum of PE file from header + virtual uint32_t get_checksum() const; + + //Sets checksum of PE file + virtual void set_checksum(uint32_t checksum); + + //Returns timestamp of PE file from header + virtual uint32_t get_time_date_stamp() const; + + //Sets timestamp of PE file + virtual void set_time_date_stamp(uint32_t timestamp); + + //Returns Machine field value of PE file from header + virtual uint16_t get_machine() const; + + //Sets Machine field value of PE file + virtual void set_machine(uint16_t machine); + + //Returns DLL Characteristics + virtual uint16_t get_dll_characteristics() const; + + //Sets DLL Characteristics + virtual void set_dll_characteristics(uint16_t characteristics); + + //Sets required operation system version + virtual void set_os_version(uint16_t major, uint16_t minor); + + //Returns required operation system version (minor word) + virtual uint16_t get_minor_os_version() const; + + //Returns required operation system version (major word) + virtual uint16_t get_major_os_version() const; + + //Sets required subsystem version + virtual void set_subsystem_version(uint16_t major, uint16_t minor); + + //Returns required subsystem version (minor word) + virtual uint16_t get_minor_subsystem_version() const; + + //Returns required subsystem version (major word) + virtual uint16_t get_major_subsystem_version() const; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const; + virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const; + + //Relative Virtual Address (RVA) to Virtual Address (VA) convertions + //for PE32 and PE64 respectively + virtual uint32_t rva_to_va_32(uint32_t rva) const; + virtual uint64_t rva_to_va_64(uint32_t rva) const; + + +public: //SECTIONS + //Returns number of sections + virtual uint16_t get_number_of_sections() const; + +protected: + typename PEClassType::NtHeaders nt_headers_; //NT headers (PE32 or PE64) + +public: + //Sets number of sections + virtual void set_number_of_sections(uint16_t number); + //Sets virtual size of image + virtual void set_size_of_image(uint32_t size); + //Sets size of headers + virtual void set_size_of_headers(uint32_t size); + //Sets size of optional headers + virtual void set_size_of_optional_header(uint16_t size); + //Returns nt headers data pointer + virtual char* get_nt_headers_ptr(); + //Returns nt headers data pointer + virtual const char* get_nt_headers_ptr() const; + //Returns size of NT header + virtual uint32_t get_sizeof_nt_header() const; + //Returns size of optional headers + virtual uint32_t get_sizeof_opt_headers() const; + //Sets file alignment (no checks) + virtual void set_file_alignment_unchecked(uint32_t alignment); + //Sets base of code + virtual void set_base_of_code(uint32_t base); + //Returns base of code + virtual uint32_t get_base_of_code() const; + //Returns needed PE magic for PE or PE+ (from template parameters) + virtual uint32_t get_needed_magic() const; +}; + +//Two used typedefs for PE32 (PE) and PE64 (PE+) +typedef pe_types<pe_win::image_nt_headers32, + pe_win::image_optional_header32, + pe_win::image_nt_optional_hdr32_magic, + uint32_t, + pe_win::image_ordinal_flag32, + pe_win::image_tls_directory32, + pe_win::image_load_config_directory32> pe_types_class_32; + +typedef pe_types<pe_win::image_nt_headers64, + pe_win::image_optional_header64, + pe_win::image_nt_optional_hdr64_magic, + uint64_t, + pe_win::image_ordinal_flag64, + pe_win::image_tls_directory64, + pe_win::image_load_config_directory64> pe_types_class_64; + +typedef pe_properties_generic<pe_types_class_32> pe_properties_32; +typedef pe_properties_generic<pe_types_class_64> pe_properties_64; +} diff --git a/pe_lib/pe_rebuilder.cpp b/pe_lib/pe_rebuilder.cpp new file mode 100644 index 0000000..9853a00 --- /dev/null +++ b/pe_lib/pe_rebuilder.cpp @@ -0,0 +1,177 @@ +#include "pe_rebuilder.h" +#include "pe_base.h" +#include "pe_structures.h" +#include "pe_exception.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Rebuilds PE image headers +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, image_dos_header& dos_header, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + dos_header = pe.get_dos_header(); + + if(strip_dos_header) + { + //Strip stub overlay + pe.strip_stub_overlay(); + //BaseOfCode NT Headers field now overlaps + //e_lfanew field, so we're acrually setting + //e_lfanew with this call + pe.set_base_of_code(8 * sizeof(uint16_t)); + } + else + { + //Set start of PE headers + dos_header.e_lfanew = sizeof(image_dos_header) + + pe_utils::align_up(static_cast<uint32_t>(pe.get_stub_overlay().size()), sizeof(uint32_t)); + } + + section_list& sections = pe.get_image_sections(); + + //Calculate pointer to section data + size_t ptr_to_section_data = (strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)) + pe.get_sizeof_nt_header() + + pe_utils::align_up(pe.get_stub_overlay().size(), sizeof(uint32_t)) + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()) + + sections.size() * sizeof(image_section_header); + + if(save_bound_import && pe.has_bound_import()) + { + //It will be aligned to DWORD, because we're aligning to DWORD everything above it + pe.set_directory_rva(image_directory_entry_bound_import, static_cast<uint32_t>(ptr_to_section_data)); + ptr_to_section_data += pe.get_directory_size(image_directory_entry_bound_import); + } + + ptr_to_section_data = pe_utils::align_up(ptr_to_section_data, pe.get_file_alignment()); + + //Set size of headers and size of optional header + if(change_size_of_headers) + { + if(!pe.get_image_sections().empty()) + { + if(static_cast<uint32_t>(ptr_to_section_data) > (*sections.begin()).get_virtual_address()) + throw pe_exception("Headers of PE file are too long. Try to strip STUB or don't build bound import", pe_exception::cannot_rebuild_image); + } + + pe.set_size_of_headers(static_cast<uint32_t>(ptr_to_section_data)); + } + + //Set number of sections in PE header + pe.update_number_of_sections(); + + pe.update_image_size(); + + pe.set_size_of_optional_header(static_cast<uint16_t>(pe.get_sizeof_opt_headers() + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()))); + + //Recalculate pointer to raw data according to section list + for(section_list::iterator it = sections.begin(); it != sections.end(); ++it) + { + //Save section headers PointerToRawData + (*it).set_pointer_to_raw_data(static_cast<uint32_t>(ptr_to_section_data)); + ptr_to_section_data += (*it).get_aligned_raw_size(pe.get_file_alignment()); + } +} + +//Rebuild PE image and write it to "out" ostream +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + if(out.bad()) + throw pe_exception("Stream is bad", pe_exception::stream_is_bad); + + if(save_bound_import && pe.has_bound_import()) + { + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true) + < pe.get_directory_size(image_directory_entry_bound_import)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + } + + //Change ostream state + out.exceptions(std::ios::goodbit); + out.clear(); + + uint32_t original_bound_import_rva = pe.has_bound_import() ? pe.get_directory_rva(image_directory_entry_bound_import) : 0; + if(original_bound_import_rva && original_bound_import_rva > pe.get_size_of_headers()) + { + //No need to do anything with bound import directory + //if it is placed inside of any section, not headers + original_bound_import_rva = 0; + save_bound_import = false; + } + + { + image_dos_header dos_header; + + //Rebuild PE image headers + rebuild_pe(pe, dos_header, strip_dos_header, change_size_of_headers, save_bound_import); + + //Write DOS header + out.write(reinterpret_cast<const char*>(&dos_header), strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)); + } + + //If we have stub overlay, write it too + { + const std::string& stub = pe.get_stub_overlay(); + if(stub.size()) + { + out.write(stub.data(), stub.size()); + size_t aligned_size = pe_utils::align_up(stub.size(), sizeof(uint32_t)); + //Align PE header, which is right after rich overlay + while(aligned_size > stub.size()) + { + out.put('\0'); + --aligned_size; + } + } + } + + //Write NT headers + out.write(static_cast<const pe_base&>(pe).get_nt_headers_ptr(), pe.get_sizeof_nt_header() + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes())); + + //Write section headers + const section_list& sections = pe.get_image_sections(); + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + { + if(it == sections.end() - 1) //If last section encountered + { + image_section_header header((*it).get_raw_header()); + header.SizeOfRawData = static_cast<uint32_t>((*it).get_raw_data().length()); //Set non-aligned actual data length for it + out.write(reinterpret_cast<const char*>(&header), sizeof(image_section_header)); + } + else + { + out.write(reinterpret_cast<const char*>(&(*it).get_raw_header()), sizeof(image_section_header)); + } + } + + //Write bound import data if requested + if(save_bound_import && pe.has_bound_import()) + { + out.write(pe.section_data_from_rva(original_bound_import_rva, section_data_raw, true), + pe.get_directory_size(image_directory_entry_bound_import)); + } + + //Write section data finally + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + { + const section& s = *it; + + std::streamoff wpos = out.tellp(); + + //Fill unused overlay data between sections with null bytes + for(unsigned int i = 0; i < s.get_pointer_to_raw_data() - wpos; i++) + out.put(0); + + //Write raw section data + out.write(s.get_raw_data().data(), s.get_raw_data().length()); + } +} +} diff --git a/pe_lib/pe_rebuilder.h b/pe_lib/pe_rebuilder.h new file mode 100644 index 0000000..772bc7b --- /dev/null +++ b/pe_lib/pe_rebuilder.h @@ -0,0 +1,11 @@ +#pragma once +#include <ostream> + +namespace pe_bliss +{ +class pe_base; +//Rebuilds PE image, writes resulting image to ostream "out". If strip_dos_header == true, DOS header will be stripped a little +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header = false, bool change_size_of_headers = true, bool save_bound_import = true); +} diff --git a/pe_lib/pe_relocations.cpp b/pe_lib/pe_relocations.cpp new file mode 100644 index 0000000..49636e8 --- /dev/null +++ b/pe_lib/pe_relocations.cpp @@ -0,0 +1,299 @@ +#include <string.h> +#include "pe_relocations.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//RELOCATIONS +//Default constructor +relocation_entry::relocation_entry() + :rva_(0), type_(0) +{} + +//Constructor from relocation item (WORD) +relocation_entry::relocation_entry(uint16_t relocation_value) + :rva_(relocation_value & ((1 << 12) - 1)), type_(relocation_value >> 12) +{} + +//Constructor from relative rva and relocation type +relocation_entry::relocation_entry(uint16_t rrva, uint16_t type) + :rva_(rrva), type_(type) +{} + +//Returns RVA of relocation +uint16_t relocation_entry::get_rva() const +{ + return rva_; +} + +//Returns type of relocation +uint16_t relocation_entry::get_type() const +{ + return type_; +} + +//Sets RVA of relocation +void relocation_entry::set_rva(uint16_t rva) +{ + rva_ = rva; +} + +//Sets type of relocation +void relocation_entry::set_type(uint16_t type) +{ + type_ = type; +} + +//Returns relocation item (rrva + type) +uint16_t relocation_entry::get_item() const +{ + return rva_ | (type_ << 12); +} + +//Sets relocation item (rrva + type) +void relocation_entry::set_item(uint16_t item) +{ + rva_ = item & ((1 << 12) - 1); + type_ = item >> 12; +} + +//Returns relocation list +const relocation_table::relocation_list& relocation_table::get_relocations() const +{ + return relocations_; +} + +//Adds relocation to table +void relocation_table::add_relocation(const relocation_entry& entry) +{ + relocations_.push_back(entry); +} + +//Default constructor +relocation_table::relocation_table() + :rva_(0) +{} + +//Constructor from RVA of relocation table +relocation_table::relocation_table(uint32_t rva) + :rva_(rva) +{} + +//Returns RVA of block +uint32_t relocation_table::get_rva() const +{ + return rva_; +} + +//Sets RVA of block +void relocation_table::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Returns changeable relocation list +relocation_table::relocation_list& relocation_table::get_relocations() +{ + return relocations_; +} + +//Get relocation list of pe file, supports one-word sized relocations only +//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed +const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries) +{ + relocation_table_list ret; + + //If image does not have relocations + if(!pe.has_reloc()) + return ret; + + //Check the length in bytes of the section containing relocation directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_basereloc), + pe.get_directory_rva(image_directory_entry_basereloc), section_data_virtual, true) + < sizeof(image_base_relocation)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_basereloc); + //First IMAGE_BASE_RELOCATION table + image_base_relocation reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); + + if(reloc_table.SizeOfBlock % 2) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + unsigned long reloc_size = pe.get_directory_size(image_directory_entry_basereloc); + unsigned long read_size = 0; + + //reloc_table.VirtualAddress is not checked (not so important) + while(reloc_table.SizeOfBlock && read_size < reloc_size) + { + //Create relocation table + relocation_table table; + //Save RVA + table.set_rva(reloc_table.VirtualAddress); + + if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + //List all relocations + for(unsigned long i = sizeof(image_base_relocation); i < reloc_table.SizeOfBlock; i += sizeof(uint16_t)) + { + relocation_entry entry(pe.section_data_from_rva<uint16_t>(current_pos + i, section_data_virtual, true)); + if(list_absolute_entries || entry.get_type() != image_rel_based_absolute) + table.add_relocation(entry); + } + + //Save table + ret.push_back(table); + + //Go to next relocation block + if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + current_pos += reloc_table.SizeOfBlock; + read_size += reloc_table.SizeOfBlock; + reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); + } + + return ret; +} + +//Simple relocations rebuilder +//To keep PE file working, don't remove any of existing relocations in +//relocation_table_list returned by a call to get_relocations() function +//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped +//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated +//If save_to_pe_header is true, PE header will be modified automatically +const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that reloc_section is attached to this PE image + if(!pe.section_attached(reloc_section)) + throw pe_exception("Relocations section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t current_reloc_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + + uint32_t needed_size = current_reloc_data_pos - offset_from_section_start; //Calculate needed size for relocation tables + uint32_t size_delta = needed_size; + + uint32_t start_reloc_pos = current_reloc_data_pos; + + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) + { + needed_size += static_cast<uint32_t>((*it).get_relocations().size() * sizeof(uint16_t) /* relocations */ + sizeof(image_base_relocation) /* table header */); + //End of each table will be DWORD-aligned + if((start_reloc_pos + needed_size - size_delta) % sizeof(uint32_t)) + needed_size += sizeof(uint16_t); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation + } + + //Check if reloc_section is last one. If it's not, check if there's enough place for relocations data + if(&reloc_section != &*(pe.get_image_sections().end() - 1) && + (reloc_section.empty() || pe_utils::align_up(reloc_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + current_reloc_data_pos)) + throw pe_exception("Insufficient space for relocations directory", pe_exception::insufficient_space); + + std::string& raw_data = reloc_section.get_raw_data(); + + //This will be done only if reloc_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + current_reloc_data_pos) + raw_data.resize(needed_size + current_reloc_data_pos); //Expand section raw data + + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) + { + //Create relocation table header + image_base_relocation reloc; + reloc.VirtualAddress = (*it).get_rva(); + const relocation_table::relocation_list& reloc_list = (*it).get_relocations(); + reloc.SizeOfBlock = static_cast<uint32_t>(sizeof(image_base_relocation) + sizeof(uint16_t) * reloc_list.size()); + if((reloc_list.size() * sizeof(uint16_t)) % sizeof(uint32_t)) //If we must align end of relocation table + reloc.SizeOfBlock += sizeof(uint16_t); + + memcpy(&raw_data[current_reloc_data_pos], &reloc, sizeof(reloc)); + current_reloc_data_pos += sizeof(reloc); + + //Enumerate relocations in table + for(relocation_table::relocation_list::const_iterator r = reloc_list.begin(); r != reloc_list.end(); ++r) + { + //Save relocations + uint16_t reloc_value = (*r).get_item(); + memcpy(&raw_data[current_reloc_data_pos], &reloc_value, sizeof(reloc_value)); + current_reloc_data_pos += sizeof(reloc_value); + } + + if(current_reloc_data_pos % sizeof(uint32_t)) //If end of table is not DWORD-aligned + { + memset(&raw_data[current_reloc_data_pos], 0, sizeof(uint16_t)); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation + current_reloc_data_pos += sizeof(uint16_t); + } + } + + image_directory ret(pe.rva_from_section_offset(reloc_section, start_reloc_pos), needed_size - size_delta); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(reloc_section, auto_strip_last_section); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_basereloc, ret.get_rva()); + pe.set_directory_size(image_directory_entry_basereloc, ret.get_size()); + + pe.clear_characteristics_flags(image_file_relocs_stripped); + pe.set_dll_characteristics(pe.get_dll_characteristics() | image_dllcharacteristics_dynamic_base); + } + + return ret; +} + +//Recalculates image base with the help of relocation tables +void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) +{ + pe.get_pe_type() == pe_type_32 + ? rebase_image_base<pe_types_class_32>(pe, tables, new_base) + : rebase_image_base<pe_types_class_64>(pe, tables, new_base); +} + +//RELOCATIONS +//Recalculates image base with the help of relocation tables +//Recalculates VAs of DWORDS/QWORDS in image according to relocations +//Notice: if you move some critical structures like TLS, image relocations will not fix new +//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed. +//It is recommended to rebase image in the very beginning and move all structures afterwards. +template<typename PEClassType> +void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) +{ + //Get current image base value + typename PEClassType::BaseSize image_base; + pe.get_image_base(image_base); + + //ImageBase difference + typename PEClassType::BaseSize base_rel = static_cast<typename PEClassType::BaseSize>(static_cast<int64_t>(new_base) - image_base); + + //We need to fix addresses from relocation tables + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = tables.begin(); it != tables.end(); ++it) + { + const relocation_table::relocation_list& relocs = (*it).get_relocations(); + + uint32_t base_rva = (*it).get_rva(); + + //Enumerate relocations + for(relocation_table::relocation_list::const_iterator rel = relocs.begin(); rel != relocs.end(); ++rel) + { + //Skip ABSOLUTE entries + if((*rel).get_type() == pe_win::image_rel_based_absolute) + continue; + + //Recalculate value by RVA and rewrite it + uint32_t current_rva = base_rva + (*rel).get_rva(); + typename PEClassType::BaseSize value = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_rva, section_data_raw, true); + value += base_rel; + memcpy(pe.section_data_from_rva(current_rva, true), &value, sizeof(value)); + } + } + + //Finally, save new image base + pe.set_image_base_64(new_base); +} +} diff --git a/pe_lib/pe_relocations.h b/pe_lib/pe_relocations.h new file mode 100644 index 0000000..f8e1506 --- /dev/null +++ b/pe_lib/pe_relocations.h @@ -0,0 +1,101 @@ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing relocation entry +//RVA of relocation is not actually RVA, but +//(real RVA) - (RVA of table) +class relocation_entry +{ +public: + //Default constructor + relocation_entry(); + //Constructor from relocation item (WORD) + explicit relocation_entry(uint16_t relocation_value); + //Constructor from relative rva and relocation type + relocation_entry(uint16_t rrva, uint16_t type); + + //Returns RVA of relocation (actually, relative RVA from relocation table RVA) + uint16_t get_rva() const; + //Returns type of relocation + uint16_t get_type() const; + + //Returns relocation item (rrva + type) + uint16_t get_item() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild relocations using rebuild_relocations() + + //Sets RVA of relocation (actually, relative RVA from relocation table RVA) + void set_rva(uint16_t rva); + //Sets type of relocation + void set_type(uint16_t type); + + //Sets relocation item (rrva + type) + void set_item(uint16_t item); + +private: + uint16_t rva_; + uint16_t type_; +}; + +//Class representing relocation table +class relocation_table +{ +public: + typedef std::vector<relocation_entry> relocation_list; + +public: + //Default constructor + relocation_table(); + //Constructor from RVA of relocation table + explicit relocation_table(uint32_t rva); + + //Returns relocation list + const relocation_list& get_relocations() const; + //Returns RVA of block + uint32_t get_rva() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild relocations using rebuild_relocations() + + //Adds relocation to table + void add_relocation(const relocation_entry& entry); + //Returns changeable relocation list + relocation_list& get_relocations(); + //Sets RVA of block + void set_rva(uint32_t rva); + +private: + uint32_t rva_; + relocation_list relocations_; +}; + +typedef std::vector<relocation_table> relocation_table_list; + +//Get relocation list of pe file, supports one-word sized relocations only +//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed +const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries = false); + +//Simple relocations rebuilder +//To keep PE file working, don't remove any of existing relocations in +//relocation_table_list returned by a call to get_relocations() function +//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped +//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated +//If save_to_pe_header is true, PE header will be modified automatically +const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +//Recalculates image base with the help of relocation tables +//Recalculates VAs of DWORDS/QWORDS in image according to relocations +//Notice: if you move some critical structures like TLS, image relocations will not fix new +//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed. +//It is recommended to rebase image in the very beginning and move all structures afterwards. +void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base); + +template<typename PEClassType> +void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base); +} diff --git a/pe_lib/pe_resource_manager.cpp b/pe_lib/pe_resource_manager.cpp index 44ac390..0d93d25 100644 --- a/pe_lib/pe_resource_manager.cpp +++ b/pe_lib/pe_resource_manager.cpp @@ -2,2871 +2,264 @@ #include <sstream> #include <iomanip> #include <math.h> +#include <string.h> #include "pe_resource_manager.h" - -#define U16TEXT(t) reinterpret_cast<const unicode16_t*>( t ) +#include "resource_internal.h" namespace pe_bliss { using namespace pe_win; -//Root version info block key value -const u16string pe_resource_viewer::version_info_key(U16TEXT("V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0\0")); -//Default process language, UNICODE -const std::wstring version_info_viewer::default_language_translation(L"041904b0"); - -#define StringFileInfo U16TEXT("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") -#define SizeofStringFileInfo sizeof("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") -#define VarFileInfo U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0") -#define Translation U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0") - -#define VarFileInfoAligned U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") -#define TranslationAligned U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") -#define SizeofVarFileInfoAligned sizeof("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") -#define SizeofTranslationAligned sizeof("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") - -//Constructor from root resource_directory -pe_resource_viewer::pe_resource_viewer(const pe_base::resource_directory& root_directory) - :root_dir_(root_directory) +//Constructor from root resource directory +pe_resource_manager::pe_resource_manager(resource_directory& root_directory) + :pe_resource_viewer(root_directory), root_dir_edit_(root_directory) {} -//Finder helpers -bool pe_resource_viewer::has_name::operator()(const pe_base::resource_directory_entry& entry) const -{ - return entry.is_named(); -} - -bool pe_resource_viewer::has_id::operator()(const pe_base::resource_directory_entry& entry) const +resource_directory& pe_resource_manager::get_root_directory() { - return !entry.is_named(); + return root_dir_edit_; } -//Lists resource types existing in PE file (non-named only) -const pe_resource_viewer::resource_type_list pe_resource_viewer::list_resource_types() const +//Removes all resources of given type or root name +//If there's more than one directory entry of a given type, only the +//first one will be deleted (that's an unusual situation) +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource_type(resource_type type) { - resource_type_list ret; - - //Get root directory entries list - const pe_base::resource_directory::entry_list& entries = root_dir_.get_entry_list(); - for(pe_base::resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + //Search for resource type + resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type)); + if(it != entries.end()) { - //List all non-named items - if(!(*it).is_named()) - ret.push_back((*it).get_id()); + //Remove it, if found + entries.erase(it); + return true; } - return ret; -} - -//Returns true if resource type exists -bool pe_resource_viewer::resource_exists(resource_type type) const -{ - const pe_base::resource_directory::entry_list& entries = root_dir_.get_entry_list(); - return std::find_if(entries.begin(), entries.end(), pe_base::resource_directory::id_entry_finder(type)) != entries.end(); -} - -//Returns true if resource name exists -bool pe_resource_viewer::resource_exists(const std::wstring& root_name) const -{ - const pe_base::resource_directory::entry_list& entries = root_dir_.get_entry_list(); - return std::find_if(entries.begin(), entries.end(), pe_base::resource_directory::name_entry_finder(root_name)) != entries.end(); + return false; } -//Helper function to get name list from entry list -const pe_resource_viewer::resource_name_list pe_resource_viewer::get_name_list(const pe_base::resource_directory::entry_list& entries) +bool pe_resource_manager::remove_resource(const std::wstring& root_name) { - resource_name_list ret; - - for(pe_base::resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + //Search for resource type + resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name)); + if(it != entries.end()) { - //List all named items - if((*it).is_named()) - ret.push_back((*it).get_name()); + //Remove it, if found + entries.erase(it); + return true; } - return ret; + return false; } -//Helper function to get ID list from entry list -const pe_resource_viewer::resource_id_list pe_resource_viewer::get_id_list(const pe_base::resource_directory::entry_list& entries) +//Helper to remove resource +bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder) { - resource_id_list ret; - - for(pe_base::resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + //Search for resource type + resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); + if(it_type != entries_type.end()) { - //List all non-named items - if(!(*it).is_named()) - ret.push_back((*it).get_id()); - } - - return ret; -} - -//Lists resource names existing in PE file by resource type -const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(resource_type type) const -{ - return get_name_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); -} - -//Lists resource names existing in PE file by resource name -const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(const std::wstring& root_name) const -{ - return get_name_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); -} - -//Lists resource IDs existing in PE file by resource type -const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(resource_type type) const -{ - return get_id_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); -} - -//Lists resource IDs existing in PE file by resource name -const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(const std::wstring& root_name) const -{ - return get_id_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); -} - -//Returns resource count by type -unsigned long pe_resource_viewer::get_resource_count(resource_type type) const -{ - return static_cast<unsigned long>( - root_dir_ //Type directory - .entry_by_id(type) - .get_resource_directory() //Name/ID directory - .get_entry_list() - .size()); -} - -//Returns language count of resource by resource type and name -unsigned long pe_resource_viewer::get_language_count(resource_type type, const std::wstring& name) const -{ - const pe_base::resource_directory::entry_list& entries = - root_dir_ //Type directory - .entry_by_id(type) - .get_resource_directory() //Name/ID directory - .entry_by_name(name) - .get_resource_directory() //Language directory - .get_entry_list(); - - return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); -} - -//Returns language count of resource by resource names -unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, const std::wstring& name) const -{ - const pe_base::resource_directory::entry_list& entries = - root_dir_ //Type directory - .entry_by_name(root_name) - .get_resource_directory() //Name/ID directory - .entry_by_name(name) - .get_resource_directory() //Language directory - .get_entry_list(); - - return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); -} - -//Returns language count of resource by resource type and ID -unsigned long pe_resource_viewer::get_language_count(resource_type type, uint32_t id) const -{ - const pe_base::resource_directory::entry_list& entries = - root_dir_ //Type directory - .entry_by_id(type) - .get_resource_directory() //Name/ID directory - .entry_by_id(id) - .get_resource_directory() //Language directory - .get_entry_list(); - - return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); -} - -//Returns language count of resource by resource name and ID -unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, uint32_t id) const -{ - const pe_base::resource_directory::entry_list& entries = - root_dir_ //Type directory - .entry_by_name(root_name) - .get_resource_directory() //Name/ID directory - .entry_by_id(id) - .get_resource_directory() //Language directory - .get_entry_list(); - - return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); -} - -//Lists resource languages by resource type and name -const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, const std::wstring& name) const -{ - const pe_base::resource_directory::entry_list& entries = - root_dir_ //Type directory - .entry_by_id(type) - .get_resource_directory() //Name/ID directory - .entry_by_name(name) - .get_resource_directory() //Language directory - .get_entry_list(); - - return get_id_list(entries); -} - -//Lists resource languages by resource names -const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, const std::wstring& name) const -{ - const pe_base::resource_directory::entry_list& entries = - root_dir_ //Type directory - .entry_by_name(root_name) - .get_resource_directory() //Name/ID directory - .entry_by_name(name) - .get_resource_directory() //Language directory - .get_entry_list(); - - return get_id_list(entries); -} - -//Lists resource languages by resource type and ID -const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, uint32_t id) const -{ - const pe_base::resource_directory::entry_list& entries = - root_dir_ //Type directory - .entry_by_id(type) - .get_resource_directory() //Name/ID directory - .entry_by_id(id) - .get_resource_directory() //Language directory - .get_entry_list(); - - return get_id_list(entries); -} - -//Lists resource languages by resource name and ID -const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, uint32_t id) const -{ - const pe_base::resource_directory::entry_list& entries = - root_dir_ //Type directory - .entry_by_name(root_name) - .get_resource_directory() //Name/ID directory - .entry_by_id(id) - .get_resource_directory() //Language directory - .get_entry_list(); - - return get_id_list(entries); -} - -//Returns raw resource data by type, name and language -const pe_resource_viewer::resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const -{ - return resource_data_info(root_dir_ //Type directory - .entry_by_id(type) - .get_resource_directory() //Name/ID directory - .entry_by_name(name) - .get_resource_directory() //Language directory - .entry_by_id(language) - .get_data_entry()); //Data directory -} - -//Returns raw resource data by root name, name and language -const pe_resource_viewer::resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const -{ - return resource_data_info(root_dir_ //Type directory - .entry_by_name(root_name) - .get_resource_directory() //Name/ID directory - .entry_by_name(name) - .get_resource_directory() //Language directory - .entry_by_id(language) - .get_data_entry()); //Data directory -} + //Search for resource name/ID with "finder" + resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); + if(it_name != entries_name.end()) + { + //Erase resource, if found + entries_name.erase(it_name); + if(entries_name.empty()) + entries_type.erase(it_type); -//Returns raw resource data by type, ID and language -const pe_resource_viewer::resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const -{ - return resource_data_info(root_dir_ //Type directory - .entry_by_id(type) - .get_resource_directory() //Name/ID directory - .entry_by_id(id) - .get_resource_directory() //Language directory - .entry_by_id(language) - .get_data_entry()); //Data directory -} + return true; + } + } -//Returns raw resource data by root name, ID and language -const pe_resource_viewer::resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const -{ - return resource_data_info(root_dir_ //Type directory - .entry_by_name(root_name) - .get_resource_directory() //Name/ID directory - .entry_by_id(id) - .get_resource_directory() //Language directory - .entry_by_id(language) - .get_data_entry()); //Data directory + return false; } -//Returns raw resource data by type, name and index in language directory (instead of language) -const pe_resource_viewer::resource_data_info pe_resource_viewer::get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index) const +//Removes all resource languages by resource type/root name and name +//Deletes only one entry of given type and name +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name) { - const pe_base::resource_directory::entry_list& entries = root_dir_ //Type directory - .entry_by_id(type) - .get_resource_directory() //Name/ID directory - .entry_by_name(name) - .get_resource_directory() //Language directory - .get_entry_list(); - - if(entries.size() <= index) - throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); - - return resource_data_info(entries.at(index).get_data_entry()); //Data directory + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name)); } -//Returns raw resource data by root name, name and index in language directory (instead of language) -const pe_resource_viewer::resource_data_info pe_resource_viewer::get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index) const +bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name) { - const pe_base::resource_directory::entry_list& entries = root_dir_ //Type directory - .entry_by_name(root_name) - .get_resource_directory() //Name/ID directory - .entry_by_name(name) - .get_resource_directory() //Language directory - .get_entry_list(); - - if(entries.size() <= index) - throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); - - return resource_data_info(entries.at(index).get_data_entry()); //Data directory + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name)); } -//Returns raw resource data by type, ID and index in language directory (instead of language) -const pe_resource_viewer::resource_data_info pe_resource_viewer::get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index) const +//Removes all resource languages by resource type/root name and ID +//Deletes only one entry of given type and ID +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, uint32_t id) { - const pe_base::resource_directory::entry_list& entries = root_dir_ //Type directory - .entry_by_id(type) - .get_resource_directory() //Name/ID directory - .entry_by_id(id) - .get_resource_directory() //Language directory - .get_entry_list(); - - if(entries.size() <= index) - throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); - - return resource_data_info(entries.at(index).get_data_entry()); //Data directory + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id)); } -//Returns raw resource data by root name, ID and index in language directory (instead of language) -const pe_resource_viewer::resource_data_info pe_resource_viewer::get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index) const +bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id) { - const pe_base::resource_directory::entry_list& entries = root_dir_ //Type directory - .entry_by_name(root_name) - .get_resource_directory() //Name/ID directory - .entry_by_id(id) - .get_resource_directory() //Language directory - .get_entry_list(); - - if(entries.size() <= index) - throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); - - return resource_data_info(entries.at(index).get_data_entry()); //Data directory + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id)); } -//Helper function of creating bitmap header -const std::string pe_resource_viewer::create_bitmap(const std::string& resource_data) const +//Helper to remove resource +bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language) { - //Create bitmap file header - bitmapfileheader header = {0}; - header.bfType = 0x4d42; //Signature "BM" - header.bfOffBits = sizeof(bitmapfileheader) + sizeof(bitmapinfoheader); //Offset to bitmap bits - header.bfSize = static_cast<uint32_t>(sizeof(bitmapfileheader) + resource_data.length()); //Size of bitmap - - //Check size of resource data - if(resource_data.length() < sizeof(bitmapinfoheader)) - throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); - + //Search for resource type + resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); + if(it_type != entries_type.end()) { - //Get bitmap info header - const bitmapinfoheader* info = reinterpret_cast<const bitmapinfoheader*>(resource_data.data()); + //Search for resource name/ID with "finder" + resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); + if(it_name != entries_name.end()) + { + //Search for resource language + resource_directory::entry_list& entries_lang = (*it_name).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_lang = std::find_if(entries_lang.begin(), entries_lang.end(), resource_directory::id_entry_finder(language)); + if(it_lang != entries_lang.end()) + { + //Erase resource, if found + entries_lang.erase(it_lang); + if(entries_lang.empty()) + { + entries_name.erase(it_name); + if(entries_name.empty()) + entries_type.erase(it_type); + } - //If color table is present, skip it - if(info->biClrUsed != 0) - header.bfOffBits += 4 * info->biClrUsed; //Add this size to offset to bitmap bits - else if(info->biBitCount <= 8) - header.bfOffBits += 4 * static_cast<uint32_t>(pow(2.f, info->biBitCount)); //Add this size to offset to bitmap bits + return true; + } + } } - //Return final bitmap data - return std::string(reinterpret_cast<const char*>(&header), sizeof(bitmapfileheader)) + resource_data; + return false; } -//Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) -const std::string pe_resource_viewer::get_bitmap_by_name(const std::wstring& name, uint32_t index) const +//Removes resource language by resource type/root name and name +//Deletes only one entry of given type, name and language +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name, uint32_t language) { - return create_bitmap(get_resource_data_by_name(resource_bitmap, name, index).get_data()); + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name), language); } -//Returns bitmap data by name and language (minimum checks of format correctness) -const std::string pe_resource_viewer::get_bitmap_by_name(uint32_t language, const std::wstring& name) const +bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language) { - return create_bitmap(get_resource_data_by_name(language, resource_bitmap, name).get_data()); + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name), language); } -//Returns bitmap data by ID and language (minimum checks of format correctness) -const std::string pe_resource_viewer::get_bitmap_by_id_lang(uint32_t language, uint32_t id) const +//Removes recource language by resource type/root name and ID +//Deletes only one entry of given type, ID and language +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, uint32_t id, uint32_t language) { - return create_bitmap(get_resource_data_by_id(language, resource_bitmap, id).get_data()); + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id), language); } -//Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) -const std::string pe_resource_viewer::get_bitmap_by_id(uint32_t id, uint32_t index) const +bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language) { - return create_bitmap(get_resource_data_by_id(resource_bitmap, id, index).get_data()); + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id), language); } -//Helper function of creating icon headers from ICON_GROUP resource data -//Returns icon count -uint16_t pe_resource_viewer::format_icon_headers(std::string& ico_data, const std::string& resource_data) const +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, resource_type type, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) { - //Check resource data size - if(resource_data.length() < sizeof(ico_header)) - throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); - - //Get icon header - const ico_header* info = reinterpret_cast<const ico_header*>(resource_data.data()); - - //Check resource data size - if(resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) - throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); - - //Reserve memory to speed up a little - ico_data.reserve(sizeof(ico_header) + info->Count * sizeof(icondirentry)); - ico_data.append(reinterpret_cast<const char*>(info), sizeof(ico_header)); - - //Iterate over all listed icons - uint32_t offset = sizeof(ico_header) + sizeof(icondirentry) * info->Count; - for(uint16_t i = 0; i != info->Count; ++i) - { - const icon_group* group = reinterpret_cast<const icon_group*>(resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); - - //Fill icon data - icondirentry direntry; - direntry.BitCount = group->BitCount; - direntry.ColorCount = group->ColorCount; - direntry.Height = group->Height; - direntry.Planes = group->Planes; - direntry.Reserved = group->Reserved; - direntry.SizeInBytes = group->SizeInBytes; - direntry.Width = group->Width; - direntry.ImageOffset = offset; - - //Add icon header to returned value - ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(icondirentry)); - - offset += group->SizeInBytes; - } + resource_directory_entry new_type_entry; + new_type_entry.set_id(type); - //Return icon count - return info->Count; + add_resource(data, new_type_entry, resource_directory::entry_finder(type), new_entry, finder, language, codepage, timestamp); } -//Returns icon data by name and index in language directory (instead of language) (minimum checks of format correctness) -const std::string pe_resource_viewer::get_icon_by_name(const std::wstring& name, uint32_t index) const +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) { - std::string ret; - - //Get resource by name and index - const std::string data = get_resource_data_by_name(resource_icon_group, name, index).get_data(); - - //Create icon headers - uint16_t icon_count = format_icon_headers(ret, data); - - //Append icon data - for(uint16_t i = 0; i != icon_count; ++i) - { - const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); - ret += get_resource_data_by_id(resource_icon, group->Number, index).get_data(); - } - - return ret; + resource_directory_entry new_type_entry; + new_type_entry.set_name(root_name); + + add_resource(data, new_type_entry, resource_directory::entry_finder(root_name), new_entry, finder, language, codepage, timestamp); } -//Returns icon data by name and language (minimum checks of format correctness) -const std::string pe_resource_viewer::get_icon_by_name(uint32_t language, const std::wstring& name) const +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, resource_directory_entry& new_root_entry, const resource_directory::entry_finder& root_finder, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) { - std::string ret; - - //Get resource by name and language - const std::string data = get_resource_data_by_name(language, resource_icon_group, name).get_data(); - - //Create icon headers - uint16_t icon_count = format_icon_headers(ret, data); - - //Append icon data - for(uint16_t i = 0; i != icon_count; ++i) + //Search for resource type + resource_directory::entry_list* entries = &root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries->begin(), entries->end(), root_finder); + if(it == entries->end()) { - const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); - ret += get_resource_data_by_id(language, resource_icon, group->Number).get_data(); + //Add resource type directory, if it was not found + resource_directory dir; + dir.set_timestamp(timestamp); + new_root_entry.add_resource_directory(dir); + entries->push_back(new_root_entry); + it = entries->end() - 1; } - return ret; -} - -//Returns icon data by ID and language (minimum checks of format correctness) -const std::string pe_resource_viewer::get_icon_by_id_lang(uint32_t language, uint32_t id) const -{ - std::string ret; - - //Get resource by language and id - const std::string data = get_resource_data_by_id(language, resource_icon_group, id).get_data(); - - //Create icon headers - uint16_t icon_count = format_icon_headers(ret, data); - - //Append icon data - for(uint16_t i = 0; i != icon_count; ++i) + //Search for resource name/ID directory with "finder" + entries = &(*it).get_resource_directory().get_entry_list(); + it = std::find_if(entries->begin(), entries->end(), finder); + if(it == entries->end()) { - const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); - ret += get_resource_data_by_id(language, resource_icon, group->Number).get_data(); + //Add resource name/ID directory, if it was not found + resource_directory dir; + dir.set_timestamp(timestamp); + new_entry.add_resource_directory(dir); + entries->push_back(new_entry); + it = entries->end() - 1; } - return ret; -} - -//Returns icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) -const std::string pe_resource_viewer::get_icon_by_id(uint32_t id, uint32_t index) const -{ - std::string ret; - - //Get resource by id and index - const std::string data = get_resource_data_by_id(resource_icon_group, id, index).get_data(); - - //Create icon headers - uint16_t icon_count = format_icon_headers(ret, data); - - //Append icon data - for(uint16_t i = 0; i != icon_count; ++i) - { - const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); - ret += get_resource_data_by_id(resource_icon, group->Number, index).get_data(); - } + //Search for data resource entry by language + entries = &(*it).get_resource_directory().get_entry_list(); + it = std::find_if(entries->begin(), entries->end(), resource_directory::id_entry_finder(language)); + if(it != entries->end()) + entries->erase(it); //Erase it, if found - return ret; + //Add new data entry + resource_directory_entry new_dir_data_entry; + resource_data_entry data_dir(data, codepage); + new_dir_data_entry.add_data_entry(data_dir); + new_dir_data_entry.set_id(language); + entries->push_back(new_dir_data_entry); } -//Helper function of creating cursor headers -//Returns cursor count -uint16_t pe_resource_viewer::format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index) const +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) { - //Check resource data length - if(resource_data.length() < sizeof(cursor_header)) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - - const cursor_header* info = reinterpret_cast<const cursor_header*>(resource_data.data()); - - //Check resource data length - if(resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group) * info->Count) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - - //Reserve needed space to speed up a little - cur_data.reserve(sizeof(cursor_header) + info->Count * sizeof(cursordirentry)); - //Add icon header - cur_data.append(reinterpret_cast<const char*>(info), sizeof(cursor_header)); - - //Iterate over all cursors listed in cursor group - uint32_t offset = sizeof(cursor_header) + sizeof(cursordirentry) * info->Count; - for(uint16_t i = 0; i != info->Count; ++i) - { - const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); - - //Fill cursor info - cursordirentry direntry; - direntry.ColorCount = 0; //OK - direntry.Width = static_cast<uint8_t>(group->Width); - direntry.Height = static_cast<uint8_t>(group->Height) / 2; - direntry.Reserved = 0; - - //Now read hotspot data from cursor data directory - const std::string cursor = index == 0xFFFFFFFF - ? get_resource_data_by_id(language, resource_cursor, group->Number).get_data() - : get_resource_data_by_id(resource_cursor, group->Number, index).get_data(); - if(cursor.length() < 2 * sizeof(uint16_t)) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - - //Here it is - two words in the very beginning of cursor data - direntry.HotspotX = *reinterpret_cast<const uint16_t*>(cursor.data()); - direntry.HotspotY = *reinterpret_cast<const uint16_t*>(cursor.data() + sizeof(uint16_t)); - - //Fill the rest data - direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t); - direntry.ImageOffset = offset; - - //Add cursor header - cur_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry)); - - offset += group->SizeInBytes; - } + resource_directory_entry new_entry; + new_entry.set_name(name); - //Return cursor count - return info->Count; + add_resource(data, type, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); } -//Returns cursor data by name and language (minimum checks of format correctness) -const std::string pe_resource_viewer::get_cursor_by_name(uint32_t language, const std::wstring& name) const +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) { - std::string ret; - - //Get resource by name and language - const std::string resource_data = get_resource_data_by_name(language, resource_cursor_group, name).get_data(); - - //Create cursor headers - uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); - - //Add cursor data - for(uint16_t i = 0; i != cursor_count; ++i) - { - const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); - ret += get_resource_data_by_id(resource_cursor, group->Number, language).get_data().substr(2 * sizeof(uint16_t)); - } + resource_directory_entry new_entry; + new_entry.set_name(name); - return ret; + add_resource(data, root_name, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); } -//Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) -const std::string pe_resource_viewer::get_cursor_by_name(const std::wstring& name, uint32_t index) const +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) { - std::string ret; - - //Get resource by name and index - const std::string resource_data = get_resource_data_by_name(resource_cursor_group, name, index).get_data(); - - //Create cursor headers - uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); - - //Add cursor data - for(uint16_t i = 0; i != cursor_count; ++i) - { - const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); - ret += get_resource_data_by_id(resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); - } + resource_directory_entry new_entry; + new_entry.set_id(id); - return ret; + add_resource(data, type, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); } -//Returns cursor data by ID and language (minimum checks of format correctness) -const std::string pe_resource_viewer::get_cursor_by_id_lang(uint32_t language, uint32_t id) const +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) { - std::string ret; - - //Get resource by ID and language - const std::string resource_data = get_resource_data_by_id(language, resource_cursor_group, id).get_data(); - - //Create cursor headers - uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); - - //Add cursor data - for(uint16_t i = 0; i != cursor_count; ++i) - { - const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); - ret += get_resource_data_by_id(resource_cursor, group->Number, language).get_data().substr(2 * sizeof(uint16_t)); - } - - return ret; -} - -//Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) -const std::string pe_resource_viewer::get_cursor_by_id(uint32_t id, uint32_t index) const -{ - std::string ret; - - //Get resource by ID and index - const std::string resource_data = get_resource_data_by_id(resource_cursor_group, id, index).get_data(); - - //Create cursor headers - uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); - - //Add cursor data - for(uint16_t i = 0; i != cursor_count; ++i) - { - const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); - ret += get_resource_data_by_id(resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); - } - - return ret; -} - -//Returns string table data by ID and index in language directory (instead of language) -const pe_resource_viewer::string_list pe_resource_viewer::get_string_table_by_id(uint32_t id, uint32_t index) const -{ - return parse_string_list(id, get_resource_data_by_id(resource_string, id, index).get_data()); -} - -//Returns string table data by ID and language -const pe_resource_viewer::string_list pe_resource_viewer::get_string_table_by_id_lang(uint32_t language, uint32_t id) const -{ - return parse_string_list(id, get_resource_data_by_id(language, resource_string, id).get_data()); -} - -//Helper function of parsing string list table -const pe_resource_viewer::string_list pe_resource_viewer::parse_string_list(uint32_t id, const std::string& resource_data) const -{ - string_list ret; - - //16 is maximum count of strings in a string table - static const unsigned long max_string_list_entries = 16; - unsigned long passed_bytes = 0; - for(unsigned long i = 0; i != max_string_list_entries; ++i) - { - //Check resource data length - if(resource_data.length() < sizeof(uint16_t) + passed_bytes) - throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); - - //Get string length - the first WORD - uint16_t string_length = *reinterpret_cast<const uint16_t*>(resource_data.data() + passed_bytes); - passed_bytes += sizeof(uint16_t); //WORD containing string length - - //Check resource data length again - if(resource_data.length() < string_length + passed_bytes) - throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); - - if(string_length) - { - //Create and save string (UNICODE) -#ifdef PE_BLISS_WINDOWS - ret.insert( - std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated in such way - std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + passed_bytes), string_length))); -#else - ret.insert( - std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated in such way - pe_base::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + passed_bytes), string_length)))); -#endif - } - - //Go to next string - passed_bytes += string_length * 2; - } - - return ret; -} - -//Returns string from string table by ID and language -const std::wstring pe_resource_viewer::get_string_by_id_lang(uint32_t language, uint16_t id) const -{ - //List strings by string table id and language - const string_list strings(get_string_table_by_id(language, (id >> 4) + 1)); - string_list::const_iterator it = strings.find(id); //Find string by id - if(it == strings.end()) - throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); - - return (*it).second; -} - -//Returns string from string table by ID and index in language directory (instead of language) -const std::wstring pe_resource_viewer::get_string_by_id(uint16_t id, uint32_t index) const -{ - //List strings by string table id and index - const string_list strings(get_string_table_by_id((id >> 4) + 1, index)); - string_list::const_iterator it = strings.find(id); //Find string by id - if(it == strings.end()) - throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); - - return (*it).second; -} - -//Default constructor -pe_resource_viewer::message_table_item::message_table_item() - :unicode_(false) -{} - -//Constructor from ANSI string -pe_resource_viewer::message_table_item::message_table_item(const std::string& str) - :unicode_(false), ansi_str_(str) -{ - pe_base::strip_nullbytes(ansi_str_); -} - -//Constructor from UNICODE string -pe_resource_viewer::message_table_item::message_table_item(const std::wstring& str) - :unicode_(true), unicode_str_(str) -{ - pe_base::strip_nullbytes(unicode_str_); -} - -//Returns true if contained string is unicode -bool pe_resource_viewer::message_table_item::is_unicode() const -{ - return unicode_; -} - -//Returns ANSI string -const std::string& pe_resource_viewer::message_table_item::get_ansi_string() const -{ - return ansi_str_; -} - -//Returns UNICODE string -const std::wstring& pe_resource_viewer::message_table_item::get_unicode_string() const -{ - return unicode_str_; -} - -//Sets ANSI string (clears UNICODE one) -void pe_resource_viewer::message_table_item::set_string(const std::string& str) -{ - ansi_str_ = str; - pe_base::strip_nullbytes(ansi_str_); - unicode_str_.clear(); - unicode_ = false; -} - -//Sets UNICODE string (clears ANSI one) -void pe_resource_viewer::message_table_item::set_string(const std::wstring& str) -{ - unicode_str_ = str; - pe_base::strip_nullbytes(unicode_str_); - ansi_str_.clear(); - unicode_ = true; -} - -//Helper function of parsing message list table -const pe_resource_viewer::message_list pe_resource_viewer::parse_message_list(const std::string& resource_data) const -{ - message_list ret; - - //Check resource data length - if(resource_data.length() < sizeof(message_resource_data)) - throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); - - const message_resource_data* message_data = reinterpret_cast<const message_resource_data*>(resource_data.data()); - - //Check resource data length more carefully and some possible overflows - if(message_data->NumberOfBlocks >= pe_base::max_dword / sizeof(message_resource_block) - || !pe_base::is_sum_safe(message_data->NumberOfBlocks * sizeof(message_resource_block), sizeof(message_resource_data)) - || resource_data.length() < message_data->NumberOfBlocks * sizeof(message_resource_block) + sizeof(message_resource_data)) - throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); - - //Iterate over all message resource blocks - for(unsigned long i = 0; i != message_data->NumberOfBlocks; ++i) - { - //Get block - const message_resource_block* block = - reinterpret_cast<const message_resource_block*>(resource_data.data() + sizeof(message_resource_data) - sizeof(message_resource_block) + sizeof(message_resource_block) * i); - - //Check resource data length and IDs - if(resource_data.length() < block->OffsetToEntries || block->LowId > block->HighId) - throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); - - unsigned long current_pos = 0; - static const unsigned long size_of_entry_headers = 4; - //List all message resource entries in block - for(uint32_t curr_id = block->LowId; curr_id <= block->HighId; curr_id++) - { - //Check resource data length and some possible overflows - if(!pe_base::is_sum_safe(block->OffsetToEntries, current_pos) - || !pe_base::is_sum_safe(block->OffsetToEntries + current_pos, size_of_entry_headers) - || resource_data.length() < block->OffsetToEntries + current_pos + size_of_entry_headers) - throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); - - //Get entry - const message_resource_entry* entry = reinterpret_cast<const message_resource_entry*>(resource_data.data() + block->OffsetToEntries + current_pos); - - //Check resource data length and entry length and some possible overflows - if(entry->Length < size_of_entry_headers - || !pe_base::is_sum_safe(block->OffsetToEntries + current_pos, entry->Length) - || resource_data.length() < block->OffsetToEntries + current_pos + entry->Length - || entry->Length < size_of_entry_headers) - throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); - - if(entry->Flags & message_resource_unicode) - { - //If string is UNICODE - //Check its length - if(entry->Length % 2) - throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); - - //Add ID and string to message table -#ifdef PE_BLISS_WINDOWS - ret.insert(std::make_pair(curr_id, message_table_item( - std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), - (entry->Length - size_of_entry_headers) / 2) - ))); -#else - ret.insert(std::make_pair(curr_id, message_table_item( - pe_base::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), - (entry->Length - size_of_entry_headers) / 2)) - ))); -#endif - } - else - { - //If string is ANSI - //Add ID and string to message table - ret.insert(std::make_pair(curr_id, message_table_item( - std::string(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers, - entry->Length - size_of_entry_headers) - ))); - } - - //Go to next entry - current_pos += entry->Length; - } - } - - return ret; -} - -//Returns message table data by ID and index in language directory (instead of language) -const pe_resource_viewer::message_list pe_resource_viewer::get_message_table_by_id(uint32_t id, uint32_t index) const -{ - return parse_message_list(get_resource_data_by_id(resource_message_table, id, index).get_data()); -} - -//Returns message table data by ID and language -const pe_resource_viewer::message_list pe_resource_viewer::get_message_table_by_id_lang(uint32_t language, uint32_t id) const -{ - return parse_message_list(get_resource_data_by_id(language, resource_message_table, id).get_data()); -} - -//Returns aligned version block value position -uint32_t pe_resource_viewer::get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key) -{ - uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); - uint32_t ret = pe_base::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ - + base_pos - + (string_length + 1 /* nullbyte */) * 2), - sizeof(uint32_t)); - - //Check possible overflows - if(ret < base_pos || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) - throw_incorrect_version_info(); - - return ret; -} - -//Returns aligned version block first child position -uint32_t pe_resource_viewer::get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key) -{ - uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); - uint32_t ret = pe_base::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ - + base_pos - + (string_length + 1 /* nullbyte */) * 2), - sizeof(uint32_t)) - + pe_base::align_up(value_length, sizeof(uint32_t)); - - //Check possible overflows - if(ret < base_pos || ret < value_length || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) - throw_incorrect_version_info(); - - return ret; -} - -//Throws an exception (id = resource_incorrect_version_info) -void pe_resource_viewer::throw_incorrect_version_info() -{ - throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); -} - -//Returns full version information: -//file_version_info: versions and file info -//lang_string_values_map: map of version info strings with encodings -//translation_values_map: map of translations -const pe_resource_viewer::file_version_info pe_resource_viewer::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const -{ - //Fixed file version info - file_version_info ret; - - //Check resource data length - if(resource_data.length() < sizeof(version_info_block)) - throw_incorrect_version_info(); - - //Root version info block - const version_info_block* root_block = reinterpret_cast<const version_info_block*>(resource_data.data()); - - //Check root block key for null-termination and its name - if(!pe_base::is_null_terminated(root_block->Key, resource_data.length() - sizeof(uint16_t) * 3 /* headers before Key data */) - || version_info_key != reinterpret_cast<const unicode16_t*>(root_block->Key)) - throw_incorrect_version_info(); - - //If file has fixed version info - if(root_block->ValueLength) - { - //Get root block value position - uint32_t value_pos = get_version_block_value_pos(0, reinterpret_cast<const unicode16_t*>(root_block->Key)); - //Check value length - if(resource_data.length() < value_pos + sizeof(vs_fixedfileinfo)) - throw_incorrect_version_info(); - - //Get VS_FIXEDFILEINFO structure pointer - const vs_fixedfileinfo* file_info = reinterpret_cast<const vs_fixedfileinfo*>(resource_data.data() + value_pos); - //Check its signature and some other fields - if(file_info->dwSignature != vs_ffi_signature || file_info->dwStrucVersion != vs_ffi_strucversion) //Don't check if file_info->dwFileFlagsMask == VS_FFI_FILEFLAGSMASK - throw_incorrect_version_info(); - - //Save fixed version info - ret = file_version_info(*file_info); - } - - //Iterate over child elements of VS_VERSIONINFO (StringFileInfo or VarFileInfo) - for(uint32_t child_pos = get_version_block_first_child_pos(0, root_block->ValueLength, reinterpret_cast<const unicode16_t*>(root_block->Key)); - child_pos < root_block->Length;) - { - //Check block position - if(!pe_base::is_sum_safe(child_pos, sizeof(version_info_block)) - || resource_data.length() < child_pos + sizeof(version_info_block)) - throw_incorrect_version_info(); - - //Get VERSION_INFO_BLOCK structure pointer - const version_info_block* block = reinterpret_cast<const version_info_block*>(resource_data.data() + child_pos); - - //Check its length - if(block->Length == 0) - throw_incorrect_version_info(); - - //Check block key for null-termination - if(!pe_base::is_null_terminated(block->Key, resource_data.length() - child_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) - throw_incorrect_version_info(); - - u16string info_type(reinterpret_cast<const unicode16_t*>(block->Key)); - //If we encountered StringFileInfo... - if(info_type == StringFileInfo) - { - //Enumerate all string tables - for(uint32_t string_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); - string_table_pos - child_pos < block->Length;) - { - //Check string table block position - if(resource_data.length() < string_table_pos + sizeof(version_info_block)) - throw_incorrect_version_info(); - - //Get VERSION_INFO_BLOCK structure pointer for string table - const version_info_block* string_table = reinterpret_cast<const version_info_block*>(resource_data.data() + string_table_pos); - - //Check its length - if(string_table->Length == 0) - throw_incorrect_version_info(); - - //Check string table key for null-termination - if(!pe_base::is_null_terminated(string_table->Key, resource_data.length() - string_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) - throw_incorrect_version_info(); - - string_values_map new_values; - - //Enumerate all strings in the string table - for(uint32_t string_pos = get_version_block_first_child_pos(string_table_pos, string_table->ValueLength, reinterpret_cast<const unicode16_t*>(string_table->Key)); - string_pos - string_table_pos < string_table->Length;) - { - //Check string block position - if(resource_data.length() < string_pos + sizeof(version_info_block)) - throw_incorrect_version_info(); - - //Get VERSION_INFO_BLOCK structure pointer for string block - const version_info_block* string_block = reinterpret_cast<const version_info_block*>(resource_data.data() + string_pos); - - //Check its length - if(string_block->Length == 0) - throw_incorrect_version_info(); - - //Check string block key for null-termination - if(!pe_base::is_null_terminated(string_block->Key, resource_data.length() - string_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) - throw_incorrect_version_info(); - - u16string data; - //If string block has value - if(string_block->ValueLength != 0) - { - //Get value position - uint32_t value_pos = get_version_block_value_pos(string_pos, reinterpret_cast<const unicode16_t*>(string_block->Key)); - //Check it - if(resource_data.length() < value_pos + string_block->ValueLength) - throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); - - //Get UNICODE string value - data = u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + value_pos), string_block->ValueLength); - pe_base::strip_nullbytes(data); - } - - //Save name-value pair -#ifdef PE_BLISS_WINDOWS - new_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_block->Key), data)); -#else - new_values.insert(std::make_pair(pe_base::from_ucs2(reinterpret_cast<const unicode16_t*>(string_block->Key)), - pe_base::from_ucs2(data))); -#endif - - //Navigate to next string block - string_pos += pe_base::align_up(string_block->Length, sizeof(uint32_t)); - } - -#ifdef PE_BLISS_WINDOWS - string_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_table->Key), new_values)); -#else - string_values.insert(std::make_pair(pe_base::from_ucs2(reinterpret_cast<const unicode16_t*>(string_table->Key)), new_values)); -#endif - - //Navigate to next string table block - string_table_pos += pe_base::align_up(string_table->Length, sizeof(uint32_t)); - } - } - else if(info_type == VarFileInfo) //If we encountered VarFileInfo - { - for(uint32_t var_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); - var_table_pos - child_pos < block->Length;) - { - //Check var block position - if(resource_data.length() < var_table_pos + sizeof(version_info_block)) - throw_incorrect_version_info(); - - //Get VERSION_INFO_BLOCK structure pointer for var block - const version_info_block* var_table = reinterpret_cast<const version_info_block*>(resource_data.data() + var_table_pos); - - //Check its length - if(var_table->Length == 0) - throw_incorrect_version_info(); - - //Check its key for null-termination - if(!pe_base::is_null_terminated(var_table->Key, resource_data.length() - var_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) - throw_incorrect_version_info(); - - //If block is "Translation" (actually, there's no other types possible in VarFileInfo) and it has value - if(u16string(reinterpret_cast<const unicode16_t*>(var_table->Key)) == Translation && var_table->ValueLength) - { - //Get its value position - uint32_t value_pos = get_version_block_value_pos(var_table_pos, reinterpret_cast<const unicode16_t*>(var_table->Key)); - //Cherck value length - if(resource_data.length() < value_pos + var_table->ValueLength) - throw_incorrect_version_info(); - - //Get list of translations: pairs of LANGUAGE_ID - CODEPAGE_ID - for(unsigned long i = 0; i < var_table->ValueLength; i += sizeof(uint16_t) * 2) - { - //Pair of WORDs - uint16_t lang_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + i); - uint16_t codepage_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + sizeof(uint16_t) + i); - //Save translation - translations.insert(std::make_pair(lang_id, codepage_id)); - } - } - - //Navigate to next var block - var_table_pos += pe_base::align_up(var_table->Length, sizeof(uint32_t)); - } - } - else - { - throw_incorrect_version_info(); - } - - //Navigate to next element in root block - child_pos += pe_base::align_up(block->Length, sizeof(uint32_t)); - } - - return ret; -} - -//Returns full version information: -//file_version info: versions and file info -//lang_string_values_map: map of version info strings with encodings -//translation_values_map: map of translations -const pe_resource_viewer::file_version_info pe_resource_viewer::get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const -{ - const std::string& resource_data = root_dir_ //Type directory - .entry_by_id(resource_version) - .get_resource_directory() //Name/ID directory - .entry_by_id(1) - .get_resource_directory() //Language directory - .entry_by_id(language) - .get_data_entry() //Data directory - .get_data(); - - return get_version_info(string_values, translations, resource_data); -} - -//Returns full version information: -//file_version_info: versions and file info -//lang_string_values_map: map of version info strings with encodings -//translation_values_map: map of translations -const pe_resource_viewer::file_version_info pe_resource_viewer::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index) const -{ - const pe_base::resource_directory::entry_list& entries = root_dir_ //Type directory - .entry_by_id(resource_version) - .get_resource_directory() //Name/ID directory - .entry_by_id(1) - .get_resource_directory() //Language directory - .get_entry_list(); - - if(entries.size() <= index) - throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); - - return get_version_info(string_values, translations, entries.at(index).get_data_entry().get_data()); //Data directory -} - -//Default constructor -pe_resource_viewer::file_version_info::file_version_info() - :file_version_ms_(0), file_version_ls_(0), - product_version_ms_(0), product_version_ls_(0), - file_flags_(0), - file_os_(0), - file_type_(0), file_subtype_(0), - file_date_ms_(0), file_date_ls_(0) -{} - -//Constructor from Windows fixed version info structure -pe_resource_viewer::file_version_info::file_version_info(const vs_fixedfileinfo& info) - :file_version_ms_(info.dwFileVersionMS), file_version_ls_(info.dwFileVersionLS), - product_version_ms_(info.dwProductVersionMS), product_version_ls_(info.dwProductVersionLS), - file_flags_(info.dwFileFlags), - file_os_(info.dwFileOS), - file_type_(info.dwFileType), file_subtype_(info.dwFileSubtype), - file_date_ms_(info.dwFileDateMS), file_date_ls_(info.dwFileDateLS) -{} - -//Returns true if file is debug-built -bool pe_resource_viewer::file_version_info::is_debug() const -{ - return file_flags_ & vs_ff_debug ? true : false; -} - -//Returns true if file is release-built -bool pe_resource_viewer::file_version_info::is_prerelease() const -{ - return file_flags_ & vs_ff_prerelease ? true : false; -} - -//Returns true if file is patched -bool pe_resource_viewer::file_version_info::is_patched() const -{ - return file_flags_ & vs_ff_patched ? true : false; -} - -//Returns true if private build -bool pe_resource_viewer::file_version_info::is_private_build() const -{ - return file_flags_ & vs_ff_privatebuild ? true : false; -} - -//Returns true if special build -bool pe_resource_viewer::file_version_info::is_special_build() const -{ - return file_flags_ & vs_ff_specialbuild ? true : false; -} - -//Returns true if info inferred -bool pe_resource_viewer::file_version_info::is_info_inferred() const -{ - return file_flags_ & vs_ff_infoinferred ? true : false; -} - -//Retuens file flags (raw DWORD) -uint32_t pe_resource_viewer::file_version_info::get_file_flags() const -{ - return file_flags_; -} - -//Returns file version most significant DWORD -uint32_t pe_resource_viewer::file_version_info::get_file_version_ms() const -{ - return file_version_ms_; -} - -//Returns file version least significant DWORD -uint32_t pe_resource_viewer::file_version_info::get_file_version_ls() const -{ - return file_version_ls_; -} - -//Returns product version most significant DWORD -uint32_t pe_resource_viewer::file_version_info::get_product_version_ms() const -{ - return product_version_ms_; -} - -//Returns product version least significant DWORD -uint32_t pe_resource_viewer::file_version_info::get_product_version_ls() const -{ - return product_version_ls_; -} - -//Returns file OS type (raw DWORD) -uint32_t pe_resource_viewer::file_version_info::get_file_os_raw() const -{ - return file_os_; -} - -//Returns file OS type -pe_resource_viewer::file_version_info::file_os_type pe_resource_viewer::file_version_info::get_file_os() const -{ - //Determine file operation system type - switch(file_os_) - { - case vos_dos: - return file_os_dos; - - case vos_os216: - return file_os_os216; - - case vos_os232: - return file_os_os232; - - case vos_nt: - return file_os_nt; - - case vos_wince: - return file_os_wince; - - case vos__windows16: - return file_os_win16; - - case vos__pm16: - return file_os_pm16; - - case vos__pm32: - return file_os_pm32; - - case vos__windows32: - return file_os_win32; - - case vos_dos_windows16: - return file_os_dos_win16; - - case vos_dos_windows32: - return file_os_dos_win32; - - case vos_os216_pm16: - return file_os_os216_pm16; - - case vos_os232_pm32: - return file_os_os232_pm32; - - case vos_nt_windows32: - return file_os_nt_win32; - } - - return file_os_unknown; -} - -//Returns file type (raw DWORD) -uint32_t pe_resource_viewer::file_version_info::get_file_type_raw() const -{ - return file_type_; -} - -//Returns file type -pe_resource_viewer::file_version_info::file_type pe_resource_viewer::file_version_info::get_file_type() const -{ - //Determine file type - switch(file_type_) - { - case vft_app: - return file_type_application; - - case vft_dll: - return file_type_dll; - - case vft_drv: - return file_type_driver; - - case vft_font: - return file_type_font; - - case vft_vxd: - return file_type_vxd; - - case vft_static_lib: - return file_type_static_lib; - } - - return file_type_unknown; -} - -//Returns file subtype (usually non-zero for drivers and fonts) -uint32_t pe_resource_viewer::file_version_info::get_file_subtype() const -{ - return file_subtype_; -} - -//Returns file date most significant DWORD -uint32_t pe_resource_viewer::file_version_info::get_file_date_ms() const -{ - return file_date_ms_; -} - -//Returns file date least significant DWORD -uint32_t pe_resource_viewer::file_version_info::get_file_date_ls() const -{ - return file_date_ls_; -} - -//Helper to set file flag -void pe_resource_viewer::file_version_info::set_file_flag(uint32_t flag) -{ - file_flags_ |= flag; -} - -//Helper to clear file flag -void pe_resource_viewer::file_version_info::clear_file_flag(uint32_t flag) -{ - file_flags_ &= ~flag; -} - -//Helper to set or clear file flag -void pe_resource_viewer::file_version_info::set_file_flag(uint32_t flag, bool set_flag) -{ - set_flag ? set_file_flag(flag) : clear_file_flag(flag); -} - -//Sets if file is debug-built -void pe_resource_viewer::file_version_info::set_debug(bool debug) -{ - set_file_flag(vs_ff_debug, debug); -} - -//Sets if file is prerelease -void pe_resource_viewer::file_version_info::set_prerelease(bool prerelease) -{ - set_file_flag(vs_ff_prerelease, prerelease); -} - -//Sets if file is patched -void pe_resource_viewer::file_version_info::set_patched(bool patched) -{ - set_file_flag(vs_ff_patched, patched); -} - -//Sets if private build -void pe_resource_viewer::file_version_info::set_private_build(bool private_build) -{ - set_file_flag(vs_ff_privatebuild, private_build); -} - -//Sets if special build -void pe_resource_viewer::file_version_info::set_special_build(bool special_build) -{ - set_file_flag(vs_ff_specialbuild, special_build); -} - -//Sets if info inferred -void pe_resource_viewer::file_version_info::set_info_inferred(bool info_inferred) -{ - set_file_flag(vs_ff_infoinferred, info_inferred); -} - -//Sets flags (raw DWORD) -void pe_resource_viewer::file_version_info::set_file_flags(uint32_t file_flags) -{ - file_flags_ = file_flags; -} - -//Sets file version most significant DWORD -void pe_resource_viewer::file_version_info::set_file_version_ms(uint32_t file_version_ms) -{ - file_version_ms_ = file_version_ms; -} - -//Sets file version least significant DWORD -void pe_resource_viewer::file_version_info::set_file_version_ls(uint32_t file_version_ls) -{ - file_version_ls_ = file_version_ls; -} - -//Sets product version most significant DWORD -void pe_resource_viewer::file_version_info::set_product_version_ms(uint32_t product_version_ms) -{ - product_version_ms_ = product_version_ms; -} - -//Sets product version least significant DWORD -void pe_resource_viewer::file_version_info::set_product_version_ls(uint32_t product_version_ls) -{ - product_version_ls_ = product_version_ls; -} - -//Sets file OS type (raw DWORD) -void pe_resource_viewer::file_version_info::set_file_os_raw(uint32_t file_os) -{ - file_os_ = file_os; -} - -//Sets file OS type -void pe_resource_viewer::file_version_info::set_file_os(file_os_type file_os) -{ - //Determine file operation system type - switch(file_os) - { - case file_os_dos: - file_os_ = vos_dos; - return; - - case file_os_os216: - file_os_ = vos_os216; - return; - - case file_os_os232: - file_os_ = vos_os232; - return; - - case file_os_nt: - file_os_ = vos_nt; - return; - - case file_os_wince: - file_os_ = vos_wince; - return; - - case file_os_win16: - file_os_ = vos__windows16; - return; - - case file_os_pm16: - file_os_ = vos__pm16; - return; - - case file_os_pm32: - file_os_ = vos__pm32; - return; - - case file_os_win32: - file_os_ = vos__windows32; - return; - - case file_os_dos_win16: - file_os_ = vos_dos_windows16; - return; - - case file_os_dos_win32: - file_os_ = vos_dos_windows32; - return; - - case file_os_os216_pm16: - file_os_ = vos_os216_pm16; - return; - - case file_os_os232_pm32: - file_os_ = vos_os232_pm32; - return; - - case file_os_nt_win32: - file_os_ = vos_nt_windows32; - return; - - default: - return; - } -} - -//Sets file type (raw DWORD) -void pe_resource_viewer::file_version_info::set_file_type_raw(uint32_t file_type) -{ - file_type_ = file_type; -} - -//Sets file type -void pe_resource_viewer::file_version_info::set_file_type(file_type file_type) -{ - //Determine file type - switch(file_type) - { - case file_type_application: - file_type_ = vft_app; - return; - - case file_type_dll: - file_type_ = vft_dll; - return; - - case file_type_driver: - file_type_ = vft_drv; - return; - - case file_type_font: - file_type_ = vft_font; - return; - - case file_type_vxd: - file_type_ = vft_vxd; - return; - - case file_type_static_lib: - file_type_ = vft_static_lib; - return; - - default: - return; - } -} - -//Sets file subtype (usually non-zero for drivers and fonts) -void pe_resource_viewer::file_version_info::set_file_subtype(uint32_t file_subtype) -{ - file_subtype_ = file_subtype; -} - -//Sets file date most significant DWORD -void pe_resource_viewer::file_version_info::set_file_date_ms(uint32_t file_date_ms) -{ - file_date_ms_ = file_date_ms; -} - -//Sets file date least significant DWORD -void pe_resource_viewer::file_version_info::set_file_date_ls(uint32_t file_date_ls) -{ - file_date_ls_ = file_date_ls; -} - -//Constructor from root resource directory -pe_resource_manager::pe_resource_manager(pe_base::resource_directory& root_directory) - :pe_resource_viewer(root_directory), root_dir_edit_(root_directory) -{} - -//Removes all resources of given type or root name -//If there's more than one directory entry of a given type, only the -//first one will be deleted (that's an unusual situation) -//Returns true if resource was deleted -bool pe_resource_manager::remove_resource_type(resource_type type) -{ - //Search for resource type - pe_base::resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); - pe_base::resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), pe_base::resource_directory::id_entry_finder(type)); - if(it != entries.end()) - { - //Remove it, if found - entries.erase(it); - return true; - } - - return false; -} - -bool pe_resource_manager::remove_resource(const std::wstring& root_name) -{ - //Search for resource type - pe_base::resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); - pe_base::resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), pe_base::resource_directory::name_entry_finder(root_name)); - if(it != entries.end()) - { - //Remove it, if found - entries.erase(it); - return true; - } - - return false; -} - -//Helper to remove resource -bool pe_resource_manager::remove_resource(const pe_base::resource_directory::entry_finder& root_finder, const pe_base::resource_directory::entry_finder& finder) -{ - //Search for resource type - pe_base::resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); - pe_base::resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); - if(it_type != entries_type.end()) - { - //Search for resource name/ID with "finder" - pe_base::resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); - pe_base::resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); - if(it_name != entries_name.end()) - { - //Erase resource, if found - entries_name.erase(it_name); - if(entries_name.empty()) - entries_type.erase(it_type); - - return true; - } - } - - return false; -} - - //Removes all resource languages by resource type/root name and name -//Deletes only one entry of given type and name -//Returns true if resource was deleted -bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name) -{ - return remove_resource(pe_base::resource_directory::entry_finder(type), pe_base::resource_directory::entry_finder(name)); -} - -bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name) -{ - return remove_resource(pe_base::resource_directory::entry_finder(root_name), pe_base::resource_directory::entry_finder(name)); -} - -//Removes all resource languages by resource type/root name and ID -//Deletes only one entry of given type and ID -//Returns true if resource was deleted -bool pe_resource_manager::remove_resource(resource_type type, uint32_t id) -{ - return remove_resource(pe_base::resource_directory::entry_finder(type), pe_base::resource_directory::entry_finder(id)); -} - -bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id) -{ - return remove_resource(pe_base::resource_directory::entry_finder(root_name), pe_base::resource_directory::entry_finder(id)); -} - -//Helper to remove resource -bool pe_resource_manager::remove_resource(const pe_base::resource_directory::entry_finder& root_finder, const pe_base::resource_directory::entry_finder& finder, uint32_t language) -{ - //Search for resource type - pe_base::resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); - pe_base::resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); - if(it_type != entries_type.end()) - { - //Search for resource name/ID with "finder" - pe_base::resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); - pe_base::resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); - if(it_name != entries_name.end()) - { - //Search for resource language - pe_base::resource_directory::entry_list& entries_lang = (*it_name).get_resource_directory().get_entry_list(); - pe_base::resource_directory::entry_list::iterator it_lang = std::find_if(entries_lang.begin(), entries_lang.end(), pe_base::resource_directory::id_entry_finder(language)); - if(it_lang != entries_lang.end()) - { - //Erase resource, if found - entries_lang.erase(it_lang); - if(entries_lang.empty()) - { - entries_name.erase(it_name); - if(entries_name.empty()) - entries_type.erase(it_type); - } - - return true; - } - } - } - - return false; -} - -//Removes resource language by resource type/root name and name -//Deletes only one entry of given type, name and language -//Returns true if resource was deleted -bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name, uint32_t language) -{ - return remove_resource(pe_base::resource_directory::entry_finder(type), pe_base::resource_directory::entry_finder(name), language); -} - -bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language) -{ - return remove_resource(pe_base::resource_directory::entry_finder(root_name), pe_base::resource_directory::entry_finder(name), language); -} - -//Removes recource language by resource type/root name and ID -//Deletes only one entry of given type, ID and language -//Returns true if resource was deleted -bool pe_resource_manager::remove_resource(resource_type type, uint32_t id, uint32_t language) -{ - return remove_resource(pe_base::resource_directory::entry_finder(type), pe_base::resource_directory::entry_finder(id), language); -} - -bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language) -{ - return remove_resource(pe_base::resource_directory::entry_finder(root_name), pe_base::resource_directory::entry_finder(id), language); -} - -//Helper to add/replace resource -void pe_resource_manager::add_resource(const std::string& data, resource_type type, pe_base::resource_directory_entry& new_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_type_entry; - new_type_entry.set_id(type); - - add_resource(data, new_type_entry, pe_base::resource_directory::entry_finder(type), new_entry, finder, language, codepage, timestamp); -} - -//Helper to add/replace resource -void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, pe_base::resource_directory_entry& new_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_type_entry; - new_type_entry.set_name(root_name); - - add_resource(data, new_type_entry, pe_base::resource_directory::entry_finder(root_name), new_entry, finder, language, codepage, timestamp); -} - -//Helper to add/replace resource -void pe_resource_manager::add_resource(const std::string& data, pe_base::resource_directory_entry& new_root_entry, const pe_base::resource_directory::entry_finder& root_finder, pe_base::resource_directory_entry& new_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - //Search for resource type - pe_base::resource_directory::entry_list* entries = &root_dir_edit_.get_entry_list(); - pe_base::resource_directory::entry_list::iterator it = std::find_if(entries->begin(), entries->end(), root_finder); - if(it == entries->end()) - { - //Add resource type directory, if it was not found - pe_base::resource_directory dir; - dir.set_timestamp(timestamp); - new_root_entry.add_resource_directory(dir); - entries->push_back(new_root_entry); - it = entries->end() - 1; - } - - //Search for resource name/ID directory with "finder" - entries = &(*it).get_resource_directory().get_entry_list(); - it = std::find_if(entries->begin(), entries->end(), finder); - if(it == entries->end()) - { - //Add resource name/ID directory, if it was not found - pe_base::resource_directory dir; - dir.set_timestamp(timestamp); - new_entry.add_resource_directory(dir); - entries->push_back(new_entry); - it = entries->end() - 1; - } - - //Search for data resource entry by language - entries = &(*it).get_resource_directory().get_entry_list(); - it = std::find_if(entries->begin(), entries->end(), pe_base::resource_directory::id_entry_finder(language)); - if(it != entries->end()) - entries->erase(it); //Erase it, if found - - //Add new data entry - pe_base::resource_directory_entry new_dir_data_entry; - pe_base::resource_data_entry data_dir(data, codepage); - new_dir_data_entry.add_data_entry(data_dir); - new_dir_data_entry.set_id(language); - entries->push_back(new_dir_data_entry); -} - -//Adds resource. If resource already exists, replaces it -void pe_resource_manager::add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_entry; - new_entry.set_name(name); - - add_resource(data, type, new_entry, pe_base::resource_directory::entry_finder(name), language, codepage, timestamp); -} - -//Adds resource. If resource already exists, replaces it -void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_entry; - new_entry.set_name(name); - - add_resource(data, root_name, new_entry, pe_base::resource_directory::entry_finder(name), language, codepage, timestamp); -} - -//Adds resource. If resource already exists, replaces it -void pe_resource_manager::add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_entry; - new_entry.set_id(id); - - add_resource(data, type, new_entry, pe_base::resource_directory::entry_finder(id), language, codepage, timestamp); -} - -//Adds resource. If resource already exists, replaces it -void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_entry; - new_entry.set_id(id); - - add_resource(data, root_name, new_entry, pe_base::resource_directory::entry_finder(id), language, codepage, timestamp); -} - -//Adds bitmap from bitmap file data. If bitmap already exists, replaces it -//timestamp will be used for directories that will be added -void pe_resource_manager::add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - //Check bitmap data a little - if(bitmap_file.length() < sizeof(bitmapfileheader)) - throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); - - pe_base::resource_directory_entry new_entry; - new_entry.set_id(id); - - //Add bitmap - add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), resource_bitmap, new_entry, pe_base::resource_directory::entry_finder(id), language, codepage, timestamp); -} - -//Adds bitmap from bitmap file data. If bitmap already exists, replaces it -//timestamp will be used for directories that will be added -void pe_resource_manager::add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - //Check bitmap data a little - if(bitmap_file.length() < sizeof(bitmapfileheader)) - throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); - - pe_base::resource_directory_entry new_entry; - new_entry.set_name(name); - - //Add bitmap - add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), resource_bitmap, new_entry, pe_base::resource_directory::entry_finder(name), language, codepage, timestamp); -} - -//Add icon helper -void pe_resource_manager::add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, pe_base::resource_directory_entry& new_icon_group_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) -{ - //Check icon for correctness - if(icon_file.length() < sizeof(ico_header)) - throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); - - const ico_header* icon_header = reinterpret_cast<const ico_header*>(&icon_file[0]); - - unsigned long size_of_headers = sizeof(ico_header) + icon_header->Count * sizeof(icondirentry); - if(icon_file.length() < size_of_headers || icon_header->Count == 0) - throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); - - //Enumerate all icons in file - for(uint16_t i = 0; i != icon_header->Count; ++i) - { - //Check icon entries - const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); - if(icon_entry->SizeInBytes == 0 - || icon_entry->ImageOffset < size_of_headers - || !pe_base::is_sum_safe(icon_entry->ImageOffset, icon_entry->SizeInBytes) - || icon_entry->ImageOffset + icon_entry->SizeInBytes > icon_file.length()) - throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); - } - - std::string icon_group_data; - ico_header* info = 0; - - if(group_icon_info) - { - //If icon group already exists - { - icon_group_data = group_icon_info->get_data(); - codepage = group_icon_info->get_codepage(); //Don't change codepage of icon group entry - } - - //Check resource data size - if(icon_group_data.length() < sizeof(ico_header)) - throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); - - //Get icon header - info = reinterpret_cast<ico_header*>(&icon_group_data[0]); - - //Check resource data size - if(icon_group_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) - throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); - - icon_group_data.resize(sizeof(ico_header) + (info->Count + icon_header->Count) * sizeof(icon_group)); - info = reinterpret_cast<ico_header*>(&icon_group_data[0]); //In case if memory was reallocated - } - else //Entry not found - icon group doesn't exist - { - icon_group_data.resize(sizeof(ico_header) + icon_header->Count * sizeof(icon_group)); - memcpy(&icon_group_data[0], icon_header, sizeof(ico_header)); - } - - //Search for available icon IDs - std::vector<uint16_t> icon_id_list(get_icon_or_cursor_free_id_list(resource_icon, mode, icon_header->Count)); - - //Enumerate all icons in file - for(uint16_t i = 0; i != icon_header->Count; ++i) - { - const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); - icon_group group = {0}; - - //Fill icon resource header - group.BitCount = icon_entry->BitCount; - group.ColorCount = icon_entry->ColorCount; - group.Height = icon_entry->Height; - group.Planes = icon_entry->Planes; - group.Reserved = icon_entry->Reserved; - group.SizeInBytes = icon_entry->SizeInBytes; - group.Width = icon_entry->Width; - group.Number = icon_id_list.at(i); - - memcpy(&icon_group_data[sizeof(ico_header) + ((info ? info->Count : 0) + i) * sizeof(icon_group)], &group, sizeof(group)); - - //Add icon to resources - pe_base::resource_directory_entry new_entry; - new_entry.set_id(group.Number); - add_resource(icon_file.substr(icon_entry->ImageOffset, icon_entry->SizeInBytes), resource_icon, new_entry, pe_base::resource_directory::entry_finder(group.Number), language, codepage, timestamp); - } - - if(info) - info->Count += icon_header->Count; //Increase icon count, if we're adding icon to existing group - - { - //Add or replace icon group data entry - add_resource(icon_group_data, resource_icon_group, new_icon_group_entry, finder, language, codepage, timestamp); - } -} - -//Returns free icon or cursor ID list depending on icon_place_mode -const std::vector<uint16_t> pe_resource_manager::get_icon_or_cursor_free_id_list(resource_type type, icon_place_mode mode, uint32_t count) -{ - //Search for available icon/cursor IDs - std::vector<uint16_t> icon_cursor_id_list; - - try - { - //If any icon exists - //List icon IDs - std::vector<uint32_t> id_list(list_resource_ids(type)); - std::sort(id_list.begin(), id_list.end()); - - //If we are placing icon on free spaces - //I.e., icon IDs 1, 3, 4, 7, 8 already exist - //We'll place five icons on IDs 2, 5, 6, 9, 10 - if(mode != icon_place_after_max_icon_id) - { - if(!id_list.empty()) - { - //Determine and list free icon IDs - for(std::vector<uint32_t>::const_iterator it = id_list.begin(); it != id_list.end(); ++it) - { - if(it == id_list.begin()) - { - if(*it > 1) - { - for(uint16_t i = 1; i != *it; ++i) - { - icon_cursor_id_list.push_back(i); - if(icon_cursor_id_list.size() == count) - break; - } - } - } - else if(*(it - 1) - *it > 1) - { - for(uint16_t i = static_cast<uint16_t>(*(it - 1) + 1); i != static_cast<uint16_t>(*it); ++i) - { - icon_cursor_id_list.push_back(i); - if(icon_cursor_id_list.size() == count) - break; - } - } - - if(icon_cursor_id_list.size() == count) - break; - } - } - } - - uint32_t max_id = id_list.empty() ? 0 : *std::max_element(id_list.begin(), id_list.end()); - for(uint32_t i = static_cast<uint32_t>(icon_cursor_id_list.size()); i != count; ++i) - icon_cursor_id_list.push_back(static_cast<uint16_t>(++max_id)); - } - catch(const pe_exception&) //Entry not found - { - for(uint16_t i = 1; i != count + 1; ++i) - icon_cursor_id_list.push_back(i); - } - - return icon_cursor_id_list; -} - -//Add cursor helper -void pe_resource_manager::add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, pe_base::resource_directory_entry& new_cursor_group_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) -{ - //Check cursor for correctness - if(cursor_file.length() < sizeof(cursor_header)) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - - const cursor_header* cur_header = reinterpret_cast<const cursor_header*>(&cursor_file[0]); - - unsigned long size_of_headers = sizeof(cursor_header) + cur_header->Count * sizeof(cursordirentry); - if(cursor_file.length() < size_of_headers || cur_header->Count == 0) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - - //Enumerate all cursors in file - for(uint16_t i = 0; i != cur_header->Count; ++i) - { - //Check cursor entries - const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); - if(cursor_entry->SizeInBytes == 0 - || cursor_entry->ImageOffset < size_of_headers - || !pe_base::is_sum_safe(cursor_entry->ImageOffset, cursor_entry->SizeInBytes) - || cursor_entry->ImageOffset + cursor_entry->SizeInBytes > cursor_file.length()) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - } - - std::string cursor_group_data; - cursor_header* info = 0; - - if(group_cursor_info) - { - //If cursor group already exists - { - cursor_group_data = group_cursor_info->get_data(); - codepage = group_cursor_info->get_codepage(); //Don't change codepage of cursor group entry - } - - //Check resource data size - if(cursor_group_data.length() < sizeof(cursor_header)) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - - //Get cursor header - info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); - - //Check resource data size - if(cursor_group_data.length() < sizeof(cursor_header) + info->Count * sizeof(cursor_group)) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - - cursor_group_data.resize(sizeof(cursor_header) + (info->Count + cur_header->Count) * sizeof(cursor_group)); - info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); //In case if memory was reallocated - } - else //Entry not found - cursor group doesn't exist - { - cursor_group_data.resize(sizeof(cursor_header) + cur_header->Count * sizeof(cursor_group)); - memcpy(&cursor_group_data[0], cur_header, sizeof(cursor_header)); - } - - //Search for available cursor IDs - std::vector<uint16_t> cursor_id_list(get_icon_or_cursor_free_id_list(resource_cursor, mode, cur_header->Count)); - - //Enumerate all cursors in file - for(uint16_t i = 0; i != cur_header->Count; ++i) - { - const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); - cursor_group group = {0}; - - //Fill cursor resource header - group.Height = cursor_entry->Height; - group.SizeInBytes = cursor_entry->SizeInBytes; - group.Width = cursor_entry->Width; - group.Number = cursor_id_list.at(i); - - memcpy(&cursor_group_data[sizeof(cursor_header) + ((info ? info->Count : 0) + i) * sizeof(cursor_group)], &group, sizeof(group)); - - //Add cursor to resources - pe_base::resource_directory_entry new_entry; - new_entry.set_id(group.Number); - - //Fill resource data (two WORDs for hotspot of cursor, and cursor bitmap data) - std::string cur_data; - cur_data.resize(sizeof(uint16_t) * 2); - memcpy(&cur_data[0], &cursor_entry->HotspotX, sizeof(uint16_t)); - memcpy(&cur_data[sizeof(uint16_t)], &cursor_entry->HotspotY, sizeof(uint16_t)); - cur_data.append(cursor_file.substr(cursor_entry->ImageOffset, cursor_entry->SizeInBytes)); - - add_resource(cur_data, resource_cursor, new_entry, pe_base::resource_directory::entry_finder(group.Number), language, codepage, timestamp); - } - - if(info) - info->Count += cur_header->Count; //Increase cursor count, if we're adding cursor to existing group - - { - //Add or replace cursor group data entry - add_resource(cursor_group_data, resource_cursor_group, new_cursor_group_entry, finder, language, codepage, timestamp); - } -} - -//Adds icon(s) from icon file data -//timestamp will be used for directories that will be added -//If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) -//(Codepage of icon group and icons will not be changed in this case) -//icon_place_mode determines, how new icon(s) will be placed -void pe_resource_manager::add_icon(const std::string& icon_file, const std::wstring& icon_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_icon_group_entry; - new_icon_group_entry.set_name(icon_group_name); - std::auto_ptr<resource_data_info> data_info; - - try - { - data_info.reset(new resource_data_info(get_resource_data_by_name(language, resource_icon_group, icon_group_name))); - } - catch(const pe_exception&) //Entry not found - { - } - - add_icon(icon_file, data_info.get(), new_icon_group_entry, pe_base::resource_directory::entry_finder(icon_group_name), language, mode, codepage, timestamp); -} - -void pe_resource_manager::add_icon(const std::string& icon_file, uint32_t icon_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_icon_group_entry; - new_icon_group_entry.set_id(icon_group_id); - std::auto_ptr<resource_data_info> data_info; - - try - { - data_info.reset(new resource_data_info(get_resource_data_by_id(language, resource_icon_group, icon_group_id))); - } - catch(const pe_exception&) //Entry not found - { - } - - add_icon(icon_file, data_info.get(), new_icon_group_entry, pe_base::resource_directory::entry_finder(icon_group_id), language, mode, codepage, timestamp); -} - -//Adds cursor(s) from cursor file data -//timestamp will be used for directories that will be added -//If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) -//(Codepage of cursor group and cursors will not be changed in this case) -//icon_place_mode determines, how new cursor(s) will be placed -void pe_resource_manager::add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_cursor_group_entry; - new_cursor_group_entry.set_name(cursor_group_name); - std::auto_ptr<resource_data_info> data_info; - - try - { - data_info.reset(new resource_data_info(get_resource_data_by_name(language, resource_cursor_group, cursor_group_name))); - } - catch(const pe_exception&) //Entry not found - { - } - - add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, pe_base::resource_directory::entry_finder(cursor_group_name), language, mode, codepage, timestamp); -} - -void pe_resource_manager::add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) -{ - pe_base::resource_directory_entry new_cursor_group_entry; - new_cursor_group_entry.set_id(cursor_group_id); - std::auto_ptr<resource_data_info> data_info; - - try - { - data_info.reset(new resource_data_info(get_resource_data_by_id(language, resource_cursor_group, cursor_group_id))); - } - catch(const pe_exception&) //Entry not found - { - } - - add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, pe_base::resource_directory::entry_finder(cursor_group_id), language, mode, codepage, timestamp); -} - -//Remove icon group helper -void pe_resource_manager::remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language) -{ - //Check resource data size - if(icon_group_data.length() < sizeof(ico_header)) - throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); - - //Get icon header - const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_data.data()); - - uint16_t icon_count = info->Count; - - //Check resource data size - if(icon_group_data.length() < sizeof(ico_header) + icon_count * sizeof(icon_group)) - throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); - - //Remove icon data - for(uint16_t i = 0; i != icon_count; ++i) - { - const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); - remove_resource(resource_icon, group->Number, language); - } -} - -//Remove cursor group helper -void pe_resource_manager::remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language) -{ - //Check resource data size - if(cursor_group_data.length() < sizeof(cursor_header)) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - - //Get icon header - const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_data.data()); - - uint16_t cursor_count = info->Count; - - //Check resource data size - if(cursor_group_data.length() < sizeof(cursor_header) + cursor_count * sizeof(cursor_group)) - throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); - - //Remove icon data - for(uint16_t i = 0; i != cursor_count; ++i) - { - const icon_group* group = reinterpret_cast<const icon_group*>(cursor_group_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); - remove_resource(resource_cursor, group->Number, language); - } -} - -//Removes cursor group and all its cursors by name/ID and language -void pe_resource_manager::remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language) -{ - //Get resource by name and language - const std::string data = get_resource_data_by_name(language, resource_cursor_group, cursor_group_name).get_data(); - remove_cursors_from_cursor_group(data, language); - remove_resource(resource_cursor_group, cursor_group_name, language); -} - -//Removes cursor group and all its cursors by name/ID and language -void pe_resource_manager::remove_cursor_group(uint32_t cursor_group_id, uint32_t language) -{ - //Get resource by name and language - const std::string data = get_resource_data_by_id(language, resource_cursor_group, cursor_group_id).get_data(); - remove_cursors_from_cursor_group(data, language); - remove_resource(resource_cursor_group, cursor_group_id, language); -} - -//Removes icon group and all its icons by name/ID and language -void pe_resource_manager::remove_icon_group(const std::wstring& icon_group_name, uint32_t language) -{ - //Get resource by name and language - const std::string data = get_resource_data_by_name(language, resource_icon_group, icon_group_name).get_data(); - remove_icons_from_icon_group(data, language); - remove_resource(resource_icon_group, icon_group_name, language); -} - -//Removes icon group and all its icons by name/ID and language -void pe_resource_manager::remove_icon_group(uint32_t icon_group_id, uint32_t language) -{ - //Get resource by name and language - const std::string data = get_resource_data_by_id(language, resource_icon_group, icon_group_id).get_data(); - remove_icons_from_icon_group(data, language); - remove_resource(resource_icon_group, icon_group_id, language); -} - -//Removes bitmap by name/ID and language -void pe_resource_manager::remove_bitmap(const std::wstring& name, uint32_t language) -{ - remove_resource(resource_bitmap, name, language); -} - -//Removes bitmap by name/ID and language -void pe_resource_manager::remove_bitmap(uint32_t id, uint32_t language) -{ - remove_resource(resource_bitmap, id, language); -} - -//Default constructor -pe_resource_viewer::resource_data_info::resource_data_info(const std::string& data, uint32_t codepage) - :data_(data), codepage_(codepage) -{} - -//Constructor from data -pe_resource_viewer::resource_data_info::resource_data_info(const pe_base::resource_data_entry& data) - :data_(data.get_data()), codepage_(data.get_codepage()) -{} - -//Returns resource data -const std::string& pe_resource_viewer::resource_data_info::get_data() const -{ - return data_; -} - -//Returns resource codepage -uint32_t pe_resource_viewer::resource_data_info::get_codepage() const -{ - return codepage_; -} - -//Sets/replaces full version information: -//file_version_info: versions and file info -//lang_string_values_map: map of version info strings with encodings -//translation_values_map: map of translations -void pe_resource_manager::set_version_info(const file_version_info& file_info, const lang_string_values_map& string_values, const translation_values_map& translations, uint32_t language, uint32_t codepage, uint32_t timestamp) -{ - std::string version_data; - - //Calculate total size of version resource data - uint32_t total_version_info_length = - static_cast<uint32_t>(sizeof(version_info_block) - sizeof(uint16_t) + sizeof(uint16_t) /* pading */ - + (version_info_key.length() + 1) * 2 - + sizeof(vs_fixedfileinfo)); - - //If we have any strings values - if(!string_values.empty()) - { - total_version_info_length += sizeof(version_info_block) - sizeof(uint16_t); //StringFileInfo block - total_version_info_length += SizeofStringFileInfo; //Name of block (key) - - //Add required size for version strings - for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) - { - total_version_info_length += pe_base::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*table_it).first.length() + 1) * 2), sizeof(uint32_t)); //Name of child block and block size (key of string table block) - - const string_values_map& values = (*table_it).second; - for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) - { - total_version_info_length += pe_base::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*it).first.length() + 1) * 2), sizeof(uint32_t)); - total_version_info_length += pe_base::align_up(static_cast<uint32_t>(((*it).second.length() + 1) * 2), sizeof(uint32_t)); - } - } - } - - //If we have translations - if(!translations.empty()) - { - total_version_info_length += (sizeof(version_info_block) - sizeof(uint16_t)) * 2; //VarFileInfo and Translation blocks - total_version_info_length += SizeofVarFileInfoAligned; //DWORD-aligned VarFileInfo block name - total_version_info_length += SizeofTranslationAligned; //DWORD-aligned Translation block name - total_version_info_length += static_cast<uint32_t>(translations.size() * sizeof(uint16_t) * 2); - } - - //Resize version data buffer - version_data.resize(total_version_info_length); - - //Create root version block - version_info_block root_block = {0}; - root_block.ValueLength = sizeof(vs_fixedfileinfo); - root_block.Length = static_cast<uint16_t>(total_version_info_length); - - //Fill fixed file info - vs_fixedfileinfo fixed_info = {0}; - fixed_info.dwFileDateLS = file_info.get_file_date_ls(); - fixed_info.dwFileDateMS = file_info.get_file_date_ms(); - fixed_info.dwFileFlags = file_info.get_file_flags(); - fixed_info.dwFileFlagsMask = vs_ffi_fileflagsmask; - fixed_info.dwFileOS = file_info.get_file_os_raw(); - fixed_info.dwFileSubtype = file_info.get_file_subtype(); - fixed_info.dwFileType = file_info.get_file_type_raw(); - fixed_info.dwFileVersionLS = file_info.get_product_version_ls(); - fixed_info.dwFileVersionMS = file_info.get_file_version_ms(); - fixed_info.dwSignature = vs_ffi_signature; - fixed_info.dwStrucVersion = vs_ffi_strucversion; - fixed_info.dwProductVersionLS = file_info.get_product_version_ls(); - fixed_info.dwProductVersionMS = file_info.get_product_version_ms(); - - //Write root block and fixed file info to buffer - uint32_t data_ptr = 0; - memcpy(&version_data[data_ptr], &root_block, sizeof(version_info_block) - sizeof(uint16_t)); - data_ptr += sizeof(version_info_block) - sizeof(uint16_t); - memcpy(&version_data[data_ptr], version_info_key.c_str(), (version_info_key.length() + 1) * sizeof(uint16_t)); - data_ptr += static_cast<uint32_t>((version_info_key.length() + 1) * sizeof(uint16_t)); - memset(&version_data[data_ptr], 0, sizeof(uint16_t)); - data_ptr += sizeof(uint16_t); - memcpy(&version_data[data_ptr], &fixed_info, sizeof(fixed_info)); - data_ptr += sizeof(fixed_info); - - //Write string values, if any - if(!string_values.empty()) - { - //Create string file info root block - version_info_block string_file_info_block = {0}; - string_file_info_block.Type = 1; //Block type is string - memcpy(&version_data[data_ptr], &string_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); - //We will calculate its length later - version_info_block* string_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); - data_ptr += sizeof(version_info_block) - sizeof(uint16_t); - - uint32_t old_ptr1 = data_ptr; //Used to calculate string file info block length later - memcpy(&version_data[data_ptr], StringFileInfo, SizeofStringFileInfo); //Write block name - data_ptr += SizeofStringFileInfo; - - //Create string table root block (child of string file info) - version_info_block string_table_block = {0}; - string_table_block.Type = 1; //Block type is string - - for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) - { - const string_values_map& values = (*table_it).second; - - memcpy(&version_data[data_ptr], &string_table_block, sizeof(version_info_block) - sizeof(uint16_t)); - //We will calculate its length later - version_info_block* string_table_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); - data_ptr += sizeof(version_info_block) - sizeof(uint16_t); - - uint32_t old_ptr2 = data_ptr; //Used to calculate string table block length later - uint32_t lang_key_length = static_cast<uint32_t>(((*table_it).first.length() + 1) * sizeof(uint16_t)); - -#ifdef PE_BLISS_WINDOWS - memcpy(&version_data[data_ptr], (*table_it).first.c_str(), lang_key_length); //Write block key -#else - { - u16string str(pe_base::to_ucs2((*table_it).first)); - memcpy(&version_data[data_ptr], str.c_str(), lang_key_length); //Write block key - } -#endif - - data_ptr += lang_key_length; - //Align key if necessary - if((sizeof(uint16_t) * 3 + lang_key_length) % sizeof(uint32_t)) - { - memset(&version_data[data_ptr], 0, sizeof(uint16_t)); - data_ptr += sizeof(uint16_t); - } - - //Create string block (child of string table block) - version_info_block string_block = {0}; - string_block.Type = 1; //Block type is string - for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) - { - //Calculate value length and key length of string block - string_block.ValueLength = static_cast<uint16_t>((*it).second.length() + 1); - uint32_t key_length = static_cast<uint32_t>(((*it).first.length() + 1) * sizeof(uint16_t)); - //Calculate length of block - string_block.Length = static_cast<uint16_t>(pe_base::align_up(sizeof(uint16_t) * 3 + key_length, sizeof(uint32_t)) + string_block.ValueLength * sizeof(uint16_t)); - - //Write string block - memcpy(&version_data[data_ptr], &string_block, sizeof(version_info_block) - sizeof(uint16_t)); - data_ptr += sizeof(version_info_block) - sizeof(uint16_t); - -#ifdef PE_BLISS_WINDOWS - memcpy(&version_data[data_ptr], (*it).first.c_str(), key_length); //Write block key -#else - { - u16string str(pe_base::to_ucs2((*it).first)); - memcpy(&version_data[data_ptr], str.c_str(), key_length); //Write block key - } -#endif - - data_ptr += key_length; - //Align key if necessary - if((sizeof(uint16_t) * 3 + key_length) % sizeof(uint32_t)) - { - memset(&version_data[data_ptr], 0, sizeof(uint16_t)); - data_ptr += sizeof(uint16_t); - } - - //Write block data (value) -#ifdef PE_BLISS_WINDOWS - memcpy(&version_data[data_ptr], (*it).second.c_str(), string_block.ValueLength * sizeof(uint16_t)); -#else - { - u16string str(pe_base::to_ucs2((*it).second)); - memcpy(&version_data[data_ptr], str.c_str(), string_block.ValueLength * sizeof(uint16_t)); - } -#endif - - data_ptr += string_block.ValueLength * 2; - //Align data if necessary - if((string_block.ValueLength * 2) % sizeof(uint32_t)) - { - memset(&version_data[data_ptr], 0, sizeof(uint16_t)); - data_ptr += sizeof(uint16_t); - } - } - - //Calculate string table and string file info blocks lengths - string_table_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); - } - - string_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); - } - - //If we have transactions - if(!translations.empty()) - { - //Create root var file info block - version_info_block var_file_info_block = {0}; - var_file_info_block.Type = 1; //Type of block is string - //Write block header - memcpy(&version_data[data_ptr], &var_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); - //We will calculate its length later - version_info_block* var_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); - data_ptr += sizeof(version_info_block) - sizeof(uint16_t); - - uint32_t old_ptr1 = data_ptr; //Used to calculate var file info block length later - memcpy(&version_data[data_ptr], VarFileInfoAligned, SizeofVarFileInfoAligned); //Write block key (aligned) - data_ptr += SizeofVarFileInfoAligned; - - //Create root translation block (child of var file info block) - version_info_block translation_block = {0}; - //Write block header - memcpy(&version_data[data_ptr], &translation_block, sizeof(version_info_block) - sizeof(uint16_t)); - //We will calculate its length later - version_info_block* translation_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); - data_ptr += sizeof(version_info_block) - sizeof(uint16_t); - - uint32_t old_ptr2 = data_ptr; //Used to calculate var file info block length later - memcpy(&version_data[data_ptr], TranslationAligned, SizeofTranslationAligned); //Write block key (aligned) - data_ptr += SizeofTranslationAligned; - - //Calculate translation block value length - translation_block_ptr->ValueLength = static_cast<uint16_t>(sizeof(uint16_t) * 2 * translations.size()); - - //Write translation values to block - for(translation_values_map::const_iterator it = translations.begin(); it != translations.end(); ++it) - { - uint16_t lang_id = (*it).first; //Language ID - uint16_t codepage_id = (*it).second; //Codepage ID - memcpy(&version_data[data_ptr], &lang_id, sizeof(lang_id)); - data_ptr += sizeof(lang_id); - memcpy(&version_data[data_ptr], &codepage_id, sizeof(codepage_id)); - data_ptr += sizeof(codepage_id); - } - - //Calculate Translation and VarFileInfo blocks lengths - translation_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); - var_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); - } - - //Add/replace version info resource - add_resource(version_data, resource_version, 1, language, codepage, timestamp); -} - - -//Default constructor -//strings - version info strings with charsets -//translations - version info translations map -version_info_viewer::version_info_viewer(const pe_resource_viewer::lang_string_values_map& strings, - const pe_resource_viewer::translation_values_map& translations) - :strings_(strings), translations_(translations) -{} - -//Below functions have parameter translation -//If it's empty, the default language translation will be taken -//If there's no default language translation, the first one will be taken - -//Returns company name -const std::wstring version_info_viewer::get_company_name(const std::wstring& translation) const -{ - return get_property(L"CompanyName", translation); -} - -//Returns file description -const std::wstring version_info_viewer::get_file_description(const std::wstring& translation) const -{ - return get_property(L"FileDescription", translation); -} - -//Returns file version -const std::wstring version_info_viewer::get_file_version(const std::wstring& translation) const -{ - return get_property(L"FileVersion", translation); -} - -//Returns internal file name -const std::wstring version_info_viewer::get_internal_name(const std::wstring& translation) const -{ - return get_property(L"InternalName", translation); -} - -//Returns legal copyright -const std::wstring version_info_viewer::get_legal_copyright(const std::wstring& translation) const -{ - return get_property(L"LegalCopyright", translation); -} - -//Returns original file name -const std::wstring version_info_viewer::get_original_filename(const std::wstring& translation) const -{ - return get_property(L"OriginalFilename", translation); -} - -//Returns product name -const std::wstring version_info_viewer::get_product_name(const std::wstring& translation) const -{ - return get_property(L"ProductName", translation); -} - -//Returns product version -const std::wstring version_info_viewer::get_product_version(const std::wstring& translation) const -{ - return get_property(L"ProductVersion", translation); -} - -//Returns list of translations in string representation -const version_info_viewer::translation_list version_info_viewer::get_translation_list() const -{ - translation_list ret; - - //Enumerate all translations - for(pe_resource_viewer::translation_values_map::const_iterator it = translations_.begin(); it != translations_.end(); ++it) - { - //Create string representation of translation value - std::wstringstream ss; - ss << std::hex - << std::setw(4) << std::setfill(L'0') << (*it).first - << std::setw(4) << std::setfill(L'0') << (*it).second; - - //Save it - ret.push_back(ss.str()); - } - - return ret; -} - -//Returns version info property value -//property_name - required property name -//If throw_if_absent = true, will throw exception if property does not exist -//If throw_if_absent = false, will return empty string if property does not exist -const std::wstring version_info_viewer::get_property(const std::wstring& property_name, const std::wstring& translation, bool throw_if_absent) const -{ - std::wstring ret; - - //If there're no strings - if(strings_.empty()) - { - if(throw_if_absent) - throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); - - return ret; - } - - pe_resource_viewer::lang_string_values_map::const_iterator it = strings_.begin(); - - if(translation.empty()) - { - //If no translation was specified - it = strings_.find(default_language_translation); //Find default translation table - if(it == strings_.end()) //If there's no default translation table, take the first one - it = strings_.begin(); - } - else - { - it = strings_.find(translation); //Find specified translation table - if(it == strings_.end()) - { - if(throw_if_absent) - throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); - - return ret; - } - } - - //Find value of the required property - pe_resource_viewer::string_values_map::const_iterator str_it = (*it).second.find(property_name); - - if(str_it == (*it).second.end()) - { - if(throw_if_absent) - throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); - - return ret; - } - - ret = (*str_it).second; - - return ret; -} - -//Default constructor -//strings - version info strings with charsets -//translations - version info translations map -version_info_editor::version_info_editor(pe_resource_viewer::lang_string_values_map& strings, - pe_resource_viewer::translation_values_map& translations) - :version_info_viewer(strings, translations), - strings_edit_(strings), - translations_edit_(translations) -{} - -//Below functions have parameter translation -//If it's empty, the default language translation will be taken -//If there's no default language translation, the first one will be taken - -//Sets company name -void version_info_editor::set_company_name(const std::wstring& value, const std::wstring& translation) -{ - set_property(L"CompanyName", value, translation); -} - -//Sets file description -void version_info_editor::set_file_description(const std::wstring& value, const std::wstring& translation) -{ - set_property(L"FileDescription", value, translation); -} - -//Sets file version -void version_info_editor::set_file_version(const std::wstring& value, const std::wstring& translation) -{ - set_property(L"FileVersion", value, translation); -} - -//Sets internal file name -void version_info_editor::set_internal_name(const std::wstring& value, const std::wstring& translation) -{ - set_property(L"InternalName", value, translation); -} - -//Sets legal copyright -void version_info_editor::set_legal_copyright(const std::wstring& value, const std::wstring& translation) -{ - set_property(L"LegalCopyright", value, translation); -} - -//Sets original file name -void version_info_editor::set_original_filename(const std::wstring& value, const std::wstring& translation) -{ - set_property(L"OriginalFilename", value, translation); -} - -//Sets product name -void version_info_editor::set_product_name(const std::wstring& value, const std::wstring& translation) -{ - set_property(L"ProductName", value, translation); -} - -//Sets product version -void version_info_editor::set_product_version(const std::wstring& value, const std::wstring& translation) -{ - set_property(L"ProductVersion", value, translation); -} - -//Sets version info property value -//property_name - property name -//value - property value -//If translation does not exist, it will be added -//If property does not exist, it will be added -void version_info_editor::set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation) -{ - pe_resource_viewer::lang_string_values_map::iterator it = strings_edit_.begin(); - - if(translation.empty()) - { - //If no translation was specified - it = strings_edit_.find(default_language_translation); //Find default translation table - if(it == strings_edit_.end()) //If there's no default translation table, take the first one - { - it = strings_edit_.begin(); - if(it == strings_edit_.end()) //If there's no any translation table, add default one - { - it = strings_edit_.insert(std::make_pair(default_language_translation, pe_resource_viewer::string_values_map())).first; - //Also add it to translations list - add_translation(default_language_translation); - } - } - } - else - { - it = strings_edit_.find(translation); //Find specified translation table - if(it == strings_edit_.end()) //If there's no translation, add it - { - it = strings_edit_.insert(std::make_pair(translation, pe_resource_viewer::string_values_map())).first; - //Also add it to translations list - add_translation(translation); - } - } - - //Change value of the required property - ((*it).second)[property_name] = value; -} - -//Adds translation to translation list -void version_info_editor::add_translation(const std::wstring& translation) -{ - std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); - add_translation(translation_ids.first, translation_ids.second); -} - -void version_info_editor::add_translation(uint16_t language_id, uint16_t codepage_id) -{ - std::pair<pe_resource_viewer::translation_values_map::const_iterator, pe_resource_viewer::translation_values_map::const_iterator> - range(translations_edit_.equal_range(language_id)); - - //If translation already exists - for(pe_resource_viewer::translation_values_map::const_iterator it = range.first; it != range.second; ++it) - { - if((*it).second == codepage_id) - return; - } - - translations_edit_.insert(std::make_pair(language_id, codepage_id)); -} - -//Removes translation from translations and strings lists -void version_info_editor::remove_translation(const std::wstring& translation) -{ - std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); - remove_translation(translation_ids.first, translation_ids.second); -} - -void version_info_editor::remove_translation(uint16_t language_id, uint16_t codepage_id) -{ - { - //Erase string table (if exists) - std::wstringstream ss; - ss << std::hex - << std::setw(4) << std::setfill(L'0') << language_id - << std::setw(4) << std::setfill(L'0') << codepage_id; - - strings_edit_.erase(ss.str()); - } - - //Find and erase translation from translations table - std::pair<pe_resource_viewer::translation_values_map::iterator, pe_resource_viewer::translation_values_map::iterator> - it_pair = translations_edit_.equal_range(language_id); - - for(pe_resource_viewer::translation_values_map::iterator it = it_pair.first; it != it_pair.second; ++it) - { - if((*it).second == codepage_id) - { - translations_edit_.erase(it); - break; - } - } -} - -//Converts translation HEX-string to pair of language ID and codepage ID -const version_info_viewer::translation_pair version_info_viewer::translation_from_string(const std::wstring& translation) -{ - uint32_t translation_id = 0; - - { - //Convert string to DWORD - std::wstringstream ss; - ss << std::hex << translation; - ss >> translation_id; - } + resource_directory_entry new_entry; + new_entry.set_id(id); - return std::make_pair(static_cast<uint16_t>(translation_id >> 16), static_cast<uint16_t>(translation_id & 0xFFFF)); + add_resource(data, root_name, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); } } diff --git a/pe_lib/pe_resource_manager.h b/pe_lib/pe_resource_manager.h index 2ec28bf..06edde9 100644 --- a/pe_lib/pe_resource_manager.h +++ b/pe_lib/pe_resource_manager.h @@ -2,455 +2,25 @@ #include <map> #include <sstream> #include <string> -#include <string.h> #include <memory> #include "pe_base.h" #include "pe_structures.h" +#include "pe_resources.h" +#include "message_table.h" +#include "file_version_info.h" +#include "pe_resource_viewer.h" +#include "resource_data_info.h" namespace pe_bliss { -//PE resource manager allows to read resources from PE files -class pe_resource_viewer -{ -public: - //ID; string - typedef std::map<uint16_t, std::wstring> string_list; - - //Structure representing message table string - struct message_table_item - { - public: - //Default constructor - message_table_item(); - //Constructors from ANSI and UNICODE strings - explicit message_table_item(const std::string& str); - explicit message_table_item(const std::wstring& str); - - //Returns true if string is UNICODE - bool is_unicode() const; - //Returns ANSI string - const std::string& get_ansi_string() const; - //Returns UNICODE string - const std::wstring& get_unicode_string() const; - - public: - //SEets ANSI or UNICODE string - void set_string(const std::string& str); - void set_string(const std::wstring& str); - - private: - bool unicode_; - std::string ansi_str_; - std::wstring unicode_str_; - }; - - //ID; message_table_item - typedef std::map<uint32_t, message_table_item> message_list; - -public: - //Resource type enumeration - enum resource_type - { - resource_cursor = 1, - resource_bitmap = 2, - resource_icon = 3, - resource_menu = 4, - resource_dialog = 5, - resource_string = 6, - resource_fontdir = 7, - resource_font = 8, - resource_accelerator = 9, - resource_rcdata = 10, - resource_message_table = 11, - resource_cursor_group = 12, - resource_icon_group = 14, - resource_version = 16, - resource_dlginclude = 17, - resource_plugplay = 19, - resource_vxd = 20, - resource_anicursor = 21, - resource_aniicon = 22, - resource_html = 23, - resource_manifest = 24 - }; - - //Structure representing resource data - struct resource_data_info - { - public: - //Constructor from data - resource_data_info(const std::string& data, uint32_t codepage); - //Constructor from data - explicit resource_data_info(const pe_base::resource_data_entry& data); - - //Returns resource data - const std::string& get_data() const; - //Returns resource codepage - uint32_t get_codepage() const; - - private: - std::string data_; - uint32_t codepage_; - }; - -public: - //Some useful typedefs - typedef std::vector<uint32_t> resource_type_list; - typedef std::vector<uint32_t> resource_id_list; - typedef std::vector<std::wstring> resource_name_list; - typedef std::vector<uint32_t> resource_language_list; - - //Typedef for version info functions: Name - Value - typedef std::map<std::wstring, std::wstring> string_values_map; - //Typedef for version info functions: Language string - String Values Map - //Language String consists of LangID and CharsetID - //E.g. 041904b0 for Russian UNICODE, 040004b0 for Process Default Language UNICODE - typedef std::map<std::wstring, string_values_map> lang_string_values_map; - - //Typedef for version info functions: Language - Character Set - typedef std::multimap<uint16_t, uint16_t> translation_values_map; - -public: - //Constructor from root resource_directory from PE file - explicit pe_resource_viewer(const pe_base::resource_directory& root_directory); - - //Lists resource types existing in PE file (non-named only) - const resource_type_list list_resource_types() const; - //Returns true if resource type exists - bool resource_exists(resource_type type) const; - //Returns true if resource name exists - bool resource_exists(const std::wstring& root_name) const; - - //Lists resource names existing in PE file by resource type - const resource_name_list list_resource_names(resource_type type) const; - //Lists resource names existing in PE file by resource name - const resource_name_list list_resource_names(const std::wstring& root_name) const; - //Lists resource IDs existing in PE file by resource type - const resource_id_list list_resource_ids(resource_type type) const; - //Lists resource IDs existing in PE file by resource name - const resource_id_list list_resource_ids(const std::wstring& root_name) const; - //Returns resource count by type - unsigned long get_resource_count(resource_type type) const; - //Returns resource count by name - unsigned long get_resource_count(const std::wstring& root_name) const; - - //Returns language count of resource by resource type and name - unsigned long get_language_count(resource_type type, const std::wstring& name) const; - //Returns language count of resource by resource names - unsigned long get_language_count(const std::wstring& root_name, const std::wstring& name) const; - //Returns language count of resource by resource type and ID - unsigned long get_language_count(resource_type type, uint32_t id) const; - //Returns language count of resource by resource name and ID - unsigned long get_language_count(const std::wstring& root_name, uint32_t id) const; - //Lists resource languages by resource type and name - const resource_language_list list_resource_languages(resource_type type, const std::wstring& name) const; - //Lists resource languages by resource names - const resource_language_list list_resource_languages(const std::wstring& root_name, const std::wstring& name) const; - //Lists resource languages by resource type and ID - const resource_language_list list_resource_languages(resource_type type, uint32_t id) const; - //Lists resource languages by resource name and ID - const resource_language_list list_resource_languages(const std::wstring& root_name, uint32_t id) const; - - //Returns raw resource data by type, name and language - const resource_data_info get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const; - //Returns raw resource data by root name, name and language - const resource_data_info get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const; - //Returns raw resource data by type, ID and language - const resource_data_info get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const; - //Returns raw resource data by root name, ID and language - const resource_data_info get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const; - //Returns raw resource data by type, name and index in language directory (instead of language) - const resource_data_info get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index = 0) const; - //Returns raw resource data by root name, name and index in language directory (instead of language) - const resource_data_info get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index = 0) const; - //Returns raw resource data by type, ID and index in language directory (instead of language) - const resource_data_info get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index = 0) const; - //Returns raw resource data by root name, ID and index in language directory (instead of language) - const resource_data_info get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index = 0) const; - - //Returns bitmap data by name and language (minimum checks of format correctness) - const std::string get_bitmap_by_name(uint32_t language, const std::wstring& name) const; - //Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) - const std::string get_bitmap_by_name(const std::wstring& name, uint32_t index = 0) const; - //Returns bitmap data by ID and language (minimum checks of format correctness) - const std::string get_bitmap_by_id_lang(uint32_t language, uint32_t id) const; - //Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) - const std::string get_bitmap_by_id(uint32_t id, uint32_t index = 0) const; - - //Returns icon data by name and language (minimum checks of format correctness) - const std::string get_icon_by_name(uint32_t language, const std::wstring& icon_group_name) const; - //Returns icon data by name and index in language directory (instead of language) (minimum checks of format correctness) - const std::string get_icon_by_name(const std::wstring& icon_group_name, uint32_t index = 0) const; - //Returns icon data by ID and language (minimum checks of format correctness) - const std::string get_icon_by_id_lang(uint32_t language, uint32_t icon_group_id) const; - //Returns icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) - const std::string get_icon_by_id(uint32_t icon_group_id, uint32_t index = 0) const; - - //Returns cursor data by name and language (minimum checks of format correctness) - const std::string get_cursor_by_name(uint32_t language, const std::wstring& cursor_group_name) const; - //Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) - const std::string get_cursor_by_name(const std::wstring& cursor_group_name, uint32_t index = 0) const; - //Returns cursor data by ID and language (minimum checks of format correctness) - const std::string get_cursor_by_id_lang(uint32_t language, uint32_t cursor_group_id) const; - //Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) - const std::string get_cursor_by_id(uint32_t cursor_group_id, uint32_t index = 0) const; - - //Returns string table data by ID and language - const string_list get_string_table_by_id_lang(uint32_t language, uint32_t id) const; - //Returns string table data by ID and index in language directory (instead of language) - const string_list get_string_table_by_id(uint32_t id, uint32_t index = 0) const; - //Returns string from string table by ID and language - const std::wstring get_string_by_id_lang(uint32_t language, uint16_t id) const; - //Returns string from string table by ID and index in language directory (instead of language) - const std::wstring get_string_by_id(uint16_t id, uint32_t index = 0) const; - - //Returns message table data by ID and language - const message_list get_message_table_by_id_lang(uint32_t language, uint32_t id) const; - //Returns message table data by ID and index in language directory (instead of language) - const message_list get_message_table_by_id(uint32_t id, uint32_t index = 0) const; - - -public: //VERSION INFO - //Structure representing fixed file version info - struct file_version_info - { - public: - //Enumeration of file operating system types - enum file_os_type - { - file_os_unknown, - file_os_dos, - file_os_os216, - file_os_os232, - file_os_nt, - file_os_wince, - file_os_win16, - file_os_pm16, - file_os_pm32, - file_os_win32, - file_os_dos_win16, - file_os_dos_win32, - file_os_os216_pm16, - file_os_os232_pm32, - file_os_nt_win32 - }; - - //Enumeration of file types - enum file_type - { - file_type_unknown, - file_type_application, - file_type_dll, - file_type_driver, - file_type_font, - file_type_vxd, - file_type_static_lib - }; - - public: - //Default constructor - file_version_info(); - //Constructor from Windows fixed version info structure - explicit file_version_info(const pe_win::vs_fixedfileinfo& info); - - public: //Getters - //Returns true if file is debug-built - bool is_debug() const; - //Returns true if file is prerelease - bool is_prerelease() const; - //Returns true if file is patched - bool is_patched() const; - //Returns true if private build - bool is_private_build() const; - //Returns true if special build - bool is_special_build() const; - //Returns true if info inferred - bool is_info_inferred() const; - //Retuens file flags (raw DWORD) - uint32_t get_file_flags() const; - - //Returns file version most significant DWORD - uint32_t get_file_version_ms() const; - //Returns file version least significant DWORD - uint32_t get_file_version_ls() const; - //Returns product version most significant DWORD - uint32_t get_product_version_ms() const; - //Returns product version least significant DWORD - uint32_t get_product_version_ls() const; - - //Returns file OS type (raw DWORD) - uint32_t get_file_os_raw() const; - //Returns file OS type - file_os_type get_file_os() const; - - //Returns file type (raw DWORD) - uint32_t get_file_type_raw() const; - //Returns file type - file_type get_file_type() const; - - //Returns file subtype (usually non-zero for drivers and fonts) - uint32_t get_file_subtype() const; - - //Returns file date most significant DWORD - uint32_t get_file_date_ms() const; - //Returns file date least significant DWORD - uint32_t get_file_date_ls() const; - - //Returns file version string - template<typename T> - const std::basic_string<T> get_file_version_string() const - { - return get_version_string<T>(file_version_ms_, file_version_ls_); - } - - //Returns product version string - template<typename T> - const std::basic_string<T> get_product_version_string() const - { - return get_version_string<T>(product_version_ms_, product_version_ls_); - } - - public: //Setters - //Sets if file is debug-built - void set_debug(bool debug); - //Sets if file is prerelease - void set_prerelease(bool prerelease); - //Sets if file is patched - void set_patched(bool patched); - //Sets if private build - void set_private_build(bool private_build); - //Sets if special build - void set_special_build(bool special_build); - //Sets if info inferred - void set_info_inferred(bool info_inferred); - //Sets flags (raw DWORD) - void set_file_flags(uint32_t file_flags); - - //Sets file version most significant DWORD - void set_file_version_ms(uint32_t file_version_ms); - //Sets file version least significant DWORD - void set_file_version_ls(uint32_t file_version_ls); - //Sets product version most significant DWORD - void set_product_version_ms(uint32_t product_version_ms); - //Sets product version least significant DWORD - void set_product_version_ls(uint32_t product_version_ls); - - //Sets file OS type (raw DWORD) - void set_file_os_raw(uint32_t file_os); - //Sets file OS type - void set_file_os(file_os_type file_os); - - //Sets file type (raw DWORD) - void set_file_type_raw(uint32_t file_type); - //Sets file type - void set_file_type(file_type file_type); - - //Sets file subtype (usually non-zero for drivers and fonts) - void set_file_subtype(uint32_t file_subtype); - - //Sets file date most significant DWORD - void set_file_date_ms(uint32_t file_date_ms); - //Sets file date least significant DWORD - void set_file_date_ls(uint32_t file_date_ls); - - private: - //Helper to convert version DWORDs to string - template<typename T> - static const std::basic_string<T> get_version_string(uint32_t ms, uint32_t ls) - { - std::basic_stringstream<T> ss; - ss << (ms >> 16) << static_cast<T>(L'.') - << (ms & 0xFFFF) << static_cast<T>(L'.') - << (ls >> 16) << static_cast<T>(L'.') - << (ls & 0xFFFF); - return ss.str(); - } - - //Helper to set file flag - void set_file_flag(uint32_t flag); - //Helper to clear file flag - void clear_file_flag(uint32_t flag); - //Helper to set or clear file flag - void set_file_flag(uint32_t flag, bool set_flag); - - uint32_t file_version_ms_, file_version_ls_, - product_version_ms_, product_version_ls_; - uint32_t file_flags_; - uint32_t file_os_; - uint32_t file_type_, file_subtype_; - uint32_t file_date_ms_, file_date_ls_; - }; - - //Returns full version information: - //file_version_info: versions and file info - //lang_lang_string_values_map: map of version info strings with encodings with encodings - //translation_values_map: map of translations - const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index = 0) const; - const file_version_info get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const; - -protected: - //Root resource directory. We're not copying it, because it might be heavy - const pe_base::resource_directory& root_dir_; - - //Helper function of creating bitmap header - const std::string create_bitmap(const std::string& resource_data) const; - //Helper function of creating icon headers from ICON_GROUP resource data - //Returns icon count - uint16_t format_icon_headers(std::string& ico_data, const std::string& resource_data) const; - //Helper function of creating cursor headers from CURSOR_GROUP resource data - //Returns cursor count - uint16_t format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index = 0xFFFFFFFF) const; - //Helper function of parsing string list table - const string_list parse_string_list(uint32_t id, const std::string& resource_data) const; - //Helper function of parsing message list table - const message_list parse_message_list(const std::string& resource_data) const; - - //Helper function to get ID list from entry list - static const resource_id_list get_id_list(const pe_base::resource_directory::entry_list& entries); - //Helper function to get name list from entry list - static const resource_name_list get_name_list(const pe_base::resource_directory::entry_list& entries); - -protected: //VERSION INFO helpers - //L"VS_VERSION_INFO" key of root version info block - static const u16string version_info_key; - - //Returns aligned version block value position - static uint32_t get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key); - - //Returns aligned version block first child position - static uint32_t get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key); - - //Returns full version information: - //file_version_info: versions and file info - //lang_string_values_map: map of version info strings with encodings - //translation_values_map: map of translations - const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const; - - //Throws an exception (id = resource_incorrect_version_info) - static void throw_incorrect_version_info(); - -protected: - //Helper structure - finder of resource_directory_entry that is named - struct has_name - { - public: - bool operator()(const pe_base::resource_directory_entry& entry) const; - }; - - //Helper structure - finder of resource_directory_entry that is not named (has id) - struct has_id - { - public: - bool operator()(const pe_base::resource_directory_entry& entry) const; - }; -}; - //Derived class to edit PE resources class pe_resource_manager : public pe_resource_viewer { public: //Constructor from root resource directory - explicit pe_resource_manager(pe_base::resource_directory& root_directory); + explicit pe_resource_manager(resource_directory& root_directory); + + resource_directory& get_root_directory(); public: //Resource editing //Removes all resources of given type or root name @@ -491,193 +61,32 @@ public: //Resource editing void add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); void add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); - //Adds bitmap from bitmap file data. If bitmap already exists, replaces it - //timestamp will be used for directories that will be added - void add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); - void add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); - - //Removes icon group and all its icons by name/ID and language - void remove_icon_group(const std::wstring& icon_group_name, uint32_t language); - void remove_icon_group(uint32_t icon_group_id, uint32_t language); - - //Removes cursor group and all its cursors by name/ID and language - void remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language); - void remove_cursor_group(uint32_t cursor_group_id, uint32_t language); - - //Removes bitmap by name/ID and language - void remove_bitmap(const std::wstring& name, uint32_t language); - void remove_bitmap(uint32_t id, uint32_t language); - - //Dtermines, how new icon(s) or cursor(s) will be placed - enum icon_place_mode - { - icon_place_after_max_icon_id, //Icon(s) will be placed after all existing - icon_place_free_ids //New icon(s) will take all free IDs between existing icons - }; - - //Adds icon(s) from icon file data - //timestamp will be used for directories that will be added - //If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) - //(Codepage of icon group and icons will not be changed in this case) - //icon_place_mode determines, how new icon(s) will be placed - void add_icon(const std::string& icon_file, const std::wstring& icon_group_name, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); - void add_icon(const std::string& icon_file, uint32_t icon_group_id, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); - - //Adds cursor(s) from cursor file data - //timestamp will be used for directories that will be added - //If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) - //(Codepage of cursor group and cursors will not be changed in this case) - //icon_place_mode determines, how new cursor(s) will be placed - void add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); - void add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); - - //Sets/replaces full version information: - //file_version_info: versions and file info - //lang_string_values_map: map of version info strings with encodings - //translation_values_map: map of translations - void set_version_info(const file_version_info& file_info, const lang_string_values_map& string_values, const translation_values_map& translations, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); +public: + //Helpers to add/replace resource + void add_resource(const std::string& data, resource_type type, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + + void add_resource(const std::string& data, const std::wstring& root_name, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + + void add_resource(const std::string& data, resource_directory_entry& new_root_entry, + const resource_directory::entry_finder& root_finder, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); private: //Root resource directory. We're not copying it, because it might be heavy - pe_base::resource_directory& root_dir_edit_; + resource_directory& root_dir_edit_; //Helper to remove resource - bool remove_resource(const pe_base::resource_directory::entry_finder& root_finder, const pe_base::resource_directory::entry_finder& finder); + bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder); //Helper to remove resource - bool remove_resource(const pe_base::resource_directory::entry_finder& root_finder, const pe_base::resource_directory::entry_finder& finder, uint32_t language); - - //Helper to add/replace resource - void add_resource(const std::string& data, resource_type type, pe_base::resource_directory_entry& new_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp); - void add_resource(const std::string& data, const std::wstring& root_name, pe_base::resource_directory_entry& new_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp); - void add_resource(const std::string& data, pe_base::resource_directory_entry& new_root_entry, const pe_base::resource_directory::entry_finder& root_finder, pe_base::resource_directory_entry& new_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp); - - //Add icon helper - void add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, pe_base::resource_directory_entry& new_icon_group_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); - - //Add cursor helper - void add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, pe_base::resource_directory_entry& new_cursor_group_entry, const pe_base::resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); - - //Remove icon group helper - void remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language); - - //Remove cursor group helper - void remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language); - - //Returns free icon or cursor ID list depending on icon_place_mode - const std::vector<uint16_t> get_icon_or_cursor_free_id_list(resource_type type, icon_place_mode mode, uint32_t count); -}; - - -//Helper class to read version information -//lang_string_values_map: map of version info strings with encodings -//translation_values_map: map of translations -class version_info_viewer -{ -public: - //Useful typedefs - typedef std::pair<uint16_t, uint16_t> translation_pair; - typedef std::vector<std::wstring> translation_list; - -public: - //Default constructor - //strings - version info strings with charsets - //translations - version info translations map - version_info_viewer(const pe_resource_viewer::lang_string_values_map& strings, - const pe_resource_viewer::translation_values_map& translations); - - //Below functions have parameter translation - //If it's empty, the default language translation will be taken - //If there's no default language translation, the first one will be taken - - //Returns company name - const std::wstring get_company_name(const std::wstring& translation = std::wstring()) const; - //Returns file description - const std::wstring get_file_description(const std::wstring& translation = std::wstring()) const; - //Returns file version - const std::wstring get_file_version(const std::wstring& translation = std::wstring()) const; - //Returns internal file name - const std::wstring get_internal_name(const std::wstring& translation = std::wstring()) const; - //Returns legal copyright - const std::wstring get_legal_copyright(const std::wstring& translation = std::wstring()) const; - //Returns original file name - const std::wstring get_original_filename(const std::wstring& translation = std::wstring()) const; - //Returns product name - const std::wstring get_product_name(const std::wstring& translation = std::wstring()) const; - //Returns product version - const std::wstring get_product_version(const std::wstring& translation = std::wstring()) const; - - //Returns list of translations in string representation - const translation_list get_translation_list() const; - - //Returns version info property value - //property_name - required property name - //If throw_if_absent = true, will throw exception if property does not exist - //If throw_if_absent = false, will return empty string if property does not exist - const std::wstring get_property(const std::wstring& property_name, const std::wstring& translation = std::wstring(), bool throw_if_absent = false) const; - - //Converts translation HEX-string to pair of language ID and codepage ID - static const translation_pair translation_from_string(const std::wstring& translation); - -public: - //Default process language, UNICODE - static const std::wstring default_language_translation; - -private: - const pe_resource_viewer::lang_string_values_map& strings_; - const pe_resource_viewer::translation_values_map& translations_; -}; - -//Helper class to read and edit version information -//lang_string_values_map: map of version info strings with encodings -//translation_values_map: map of translations -class version_info_editor : public version_info_viewer -{ -public: - //Default constructor - //strings - version info strings with charsets - //translations - version info translations map - version_info_editor(pe_resource_viewer::lang_string_values_map& strings, - pe_resource_viewer::translation_values_map& translations); - - //Below functions have parameter translation - //If it's empty, the default language translation will be taken - //If there's no default language translation, the first one will be taken - - //Sets company name - void set_company_name(const std::wstring& value, const std::wstring& translation = std::wstring()); - //Sets file description - void set_file_description(const std::wstring& value, const std::wstring& translation = std::wstring()); - //Sets file version - void set_file_version(const std::wstring& value, const std::wstring& translation = std::wstring()); - //Sets internal file name - void set_internal_name(const std::wstring& value, const std::wstring& translation = std::wstring()); - //Sets legal copyright - void set_legal_copyright(const std::wstring& value, const std::wstring& translation = std::wstring()); - //Sets original file name - void set_original_filename(const std::wstring& value, const std::wstring& translation = std::wstring()); - //Sets product name - void set_product_name(const std::wstring& value, const std::wstring& translation = std::wstring()); - //Sets product version - void set_product_version(const std::wstring& value, const std::wstring& translation = std::wstring()); - - //Sets version info property value - //property_name - property name - //value - property value - //If translation does not exist, it will be added to strings and translations lists - //If property does not exist, it will be added - void set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation = std::wstring()); - - //Adds translation to translation list - void add_translation(const std::wstring& translation); - void add_translation(uint16_t language_id, uint16_t codepage_id); - - //Removes translation from translations and strings lists - void remove_translation(const std::wstring& translation); - void remove_translation(uint16_t language_id, uint16_t codepage_id); - -private: - pe_resource_viewer::lang_string_values_map& strings_edit_; - pe_resource_viewer::translation_values_map& translations_edit_; + bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language); }; } diff --git a/pe_lib/pe_resource_viewer.cpp b/pe_lib/pe_resource_viewer.cpp new file mode 100644 index 0000000..6e99dd3 --- /dev/null +++ b/pe_lib/pe_resource_viewer.cpp @@ -0,0 +1,361 @@ +#include <algorithm> +#include <cmath> +#include "pe_resource_viewer.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor from root resource_directory +pe_resource_viewer::pe_resource_viewer(const resource_directory& root_directory) + :root_dir_(root_directory) +{} + +const resource_directory& pe_resource_viewer::get_root_directory() const +{ + return root_dir_; +} + +//Finder helpers +bool pe_resource_viewer::has_name::operator()(const resource_directory_entry& entry) const +{ + return entry.is_named(); +} + +bool pe_resource_viewer::has_id::operator()(const resource_directory_entry& entry) const +{ + return !entry.is_named(); +} + +//Lists resource types existing in PE file (non-named only) +const pe_resource_viewer::resource_type_list pe_resource_viewer::list_resource_types() const +{ + resource_type_list ret; + + //Get root directory entries list + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all non-named items + if(!(*it).is_named()) + ret.push_back((*it).get_id()); + } + + return ret; +} + +//Returns true if resource type exists +bool pe_resource_viewer::resource_exists(resource_type type) const +{ + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + return std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type)) != entries.end(); +} + +//Returns true if resource name exists +bool pe_resource_viewer::resource_exists(const std::wstring& root_name) const +{ + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + return std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name)) != entries.end(); +} + +//Helper function to get name list from entry list +const pe_resource_viewer::resource_name_list pe_resource_viewer::get_name_list(const resource_directory::entry_list& entries) +{ + resource_name_list ret; + + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all named items + if((*it).is_named()) + ret.push_back((*it).get_name()); + } + + return ret; +} + +//Helper function to get ID list from entry list +const pe_resource_viewer::resource_id_list pe_resource_viewer::get_id_list(const resource_directory::entry_list& entries) +{ + resource_id_list ret; + + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all non-named items + if(!(*it).is_named()) + ret.push_back((*it).get_id()); + } + + return ret; +} + +//Lists resource names existing in PE file by resource type +const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(resource_type type) const +{ + return get_name_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); +} + +//Lists resource names existing in PE file by resource name +const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(const std::wstring& root_name) const +{ + return get_name_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); +} + +//Lists resource IDs existing in PE file by resource type +const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(resource_type type) const +{ + return get_id_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); +} + +//Lists resource IDs existing in PE file by resource name +const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(const std::wstring& root_name) const +{ + return get_id_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); +} + +//Returns resource count by type +unsigned long pe_resource_viewer::get_resource_count(resource_type type) const +{ + return static_cast<unsigned long>( + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .get_entry_list() + .size()); +} + +//Returns resource count by name +unsigned long pe_resource_viewer::get_resource_count(const std::wstring& root_name) const +{ + return static_cast<unsigned long>( + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .get_entry_list() + .size()); +} + +//Returns language count of resource by resource type and name +unsigned long pe_resource_viewer::get_language_count(resource_type type, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource names +unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource type and ID +unsigned long pe_resource_viewer::get_language_count(resource_type type, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource name and ID +unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Lists resource languages by resource type and name +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource names +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource type and ID +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource name and ID +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Returns raw resource data by type, name and language +const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, name and language +const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by type, ID and language +const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, ID and language +const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by type, name and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, name and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by type, ID and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, ID and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} +} diff --git a/pe_lib/pe_resource_viewer.h b/pe_lib/pe_resource_viewer.h new file mode 100644 index 0000000..1778ef0 --- /dev/null +++ b/pe_lib/pe_resource_viewer.h @@ -0,0 +1,132 @@ +#pragma once +#include <map> +#include <string> +#include "pe_structures.h" +#include "pe_resources.h" +#include "message_table.h" +#include "resource_data_info.h" + +namespace pe_bliss +{ + //PE resource manager allows to read resources from PE files +class pe_resource_viewer +{ +public: + //Resource type enumeration + enum resource_type + { + resource_cursor = 1, + resource_bitmap = 2, + resource_icon = 3, + resource_menu = 4, + resource_dialog = 5, + resource_string = 6, + resource_fontdir = 7, + resource_font = 8, + resource_accelerator = 9, + resource_rcdata = 10, + resource_message_table = 11, + resource_cursor_group = 12, + resource_icon_group = 14, + resource_version = 16, + resource_dlginclude = 17, + resource_plugplay = 19, + resource_vxd = 20, + resource_anicursor = 21, + resource_aniicon = 22, + resource_html = 23, + resource_manifest = 24 + }; + +public: + //Some useful typedefs + typedef std::vector<uint32_t> resource_type_list; + typedef std::vector<uint32_t> resource_id_list; + typedef std::vector<std::wstring> resource_name_list; + typedef std::vector<uint32_t> resource_language_list; + +public: + //Constructor from root resource_directory from PE file + explicit pe_resource_viewer(const resource_directory& root_directory); + + const resource_directory& get_root_directory() const; + + //Lists resource types existing in PE file (non-named only) + const resource_type_list list_resource_types() const; + //Returns true if resource type exists + bool resource_exists(resource_type type) const; + //Returns true if resource name exists + bool resource_exists(const std::wstring& root_name) const; + + //Lists resource names existing in PE file by resource type + const resource_name_list list_resource_names(resource_type type) const; + //Lists resource names existing in PE file by resource name + const resource_name_list list_resource_names(const std::wstring& root_name) const; + //Lists resource IDs existing in PE file by resource type + const resource_id_list list_resource_ids(resource_type type) const; + //Lists resource IDs existing in PE file by resource name + const resource_id_list list_resource_ids(const std::wstring& root_name) const; + //Returns resource count by type + unsigned long get_resource_count(resource_type type) const; + //Returns resource count by name + unsigned long get_resource_count(const std::wstring& root_name) const; + + //Returns language count of resource by resource type and name + unsigned long get_language_count(resource_type type, const std::wstring& name) const; + //Returns language count of resource by resource names + unsigned long get_language_count(const std::wstring& root_name, const std::wstring& name) const; + //Returns language count of resource by resource type and ID + unsigned long get_language_count(resource_type type, uint32_t id) const; + //Returns language count of resource by resource name and ID + unsigned long get_language_count(const std::wstring& root_name, uint32_t id) const; + //Lists resource languages by resource type and name + const resource_language_list list_resource_languages(resource_type type, const std::wstring& name) const; + //Lists resource languages by resource names + const resource_language_list list_resource_languages(const std::wstring& root_name, const std::wstring& name) const; + //Lists resource languages by resource type and ID + const resource_language_list list_resource_languages(resource_type type, uint32_t id) const; + //Lists resource languages by resource name and ID + const resource_language_list list_resource_languages(const std::wstring& root_name, uint32_t id) const; + + //Returns raw resource data by type, name and language + const resource_data_info get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const; + //Returns raw resource data by root name, name and language + const resource_data_info get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const; + //Returns raw resource data by type, ID and language + const resource_data_info get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const; + //Returns raw resource data by root name, ID and language + const resource_data_info get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const; + //Returns raw resource data by type, name and index in language directory (instead of language) + const resource_data_info get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index = 0) const; + //Returns raw resource data by root name, name and index in language directory (instead of language) + const resource_data_info get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index = 0) const; + //Returns raw resource data by type, ID and index in language directory (instead of language) + const resource_data_info get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index = 0) const; + //Returns raw resource data by root name, ID and index in language directory (instead of language) + const resource_data_info get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index = 0) const; + +protected: + //Root resource directory. We're not copying it, because it might be heavy + const resource_directory& root_dir_; + + //Helper function to get ID list from entry list + static const resource_id_list get_id_list(const resource_directory::entry_list& entries); + //Helper function to get name list from entry list + static const resource_name_list get_name_list(const resource_directory::entry_list& entries); + +protected: + //Helper structure - finder of resource_directory_entry that is named + struct has_name + { + public: + bool operator()(const resource_directory_entry& entry) const; + }; + + //Helper structure - finder of resource_directory_entry that is not named (has id) + struct has_id + { + public: + bool operator()(const resource_directory_entry& entry) const; + }; +}; +} diff --git a/pe_lib/pe_resources.cpp b/pe_lib/pe_resources.cpp new file mode 100644 index 0000000..18a67bc --- /dev/null +++ b/pe_lib/pe_resources.cpp @@ -0,0 +1,705 @@ +#include <algorithm> +#include <string.h> +#include "pe_resources.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//RESOURCES +//Default constructor +resource_data_entry::resource_data_entry() + :codepage_(0) +{} + +//Constructor from data +resource_data_entry::resource_data_entry(const std::string& data, uint32_t codepage) + :codepage_(codepage), data_(data) +{} + +//Returns resource data codepage +uint32_t resource_data_entry::get_codepage() const +{ + return codepage_; +} + +//Returns resource data +const std::string& resource_data_entry::get_data() const +{ + return data_; +} + +//Sets resource data codepage +void resource_data_entry::set_codepage(uint32_t codepage) +{ + codepage_ = codepage; +} + +//Sets resource data +void resource_data_entry::set_data(const std::string& data) +{ + data_ = data; +} + +//Default constructor +resource_directory_entry::includes::includes() + :data_(0) +{} + +//Default constructor +resource_directory_entry::resource_directory_entry() + :id_(0), includes_data_(false), named_(false) +{} + +//Copy constructor +resource_directory_entry::resource_directory_entry(const resource_directory_entry& other) + :id_(other.id_), name_(other.name_), includes_data_(other.includes_data_), named_(other.named_) +{ + //If union'ed pointer is not zero + if(other.ptr_.data_) + { + if(other.includes_data()) + ptr_.data_ = new resource_data_entry(*other.ptr_.data_); + else + ptr_.dir_ = new resource_directory(*other.ptr_.dir_); + } +} + +//Copy assignment operator +resource_directory_entry& resource_directory_entry::operator=(const resource_directory_entry& other) +{ + release(); + + id_ = other.id_; + name_ = other.name_; + includes_data_ = other.includes_data_; + named_ = other.named_; + + //If other union'ed pointer is not zero + if(other.ptr_.data_) + { + if(other.includes_data()) + ptr_.data_ = new resource_data_entry(*other.ptr_.data_); + else + ptr_.dir_ = new resource_directory(*other.ptr_.dir_); + } + + return *this; +} + +//Destroys included data +void resource_directory_entry::release() +{ + //If union'ed pointer is not zero + if(ptr_.data_) + { + if(includes_data()) + delete ptr_.data_; + else + delete ptr_.dir_; + + ptr_.data_ = 0; + } +} + +//Destructor +resource_directory_entry::~resource_directory_entry() +{ + release(); +} + +//Returns entry ID +uint32_t resource_directory_entry::get_id() const +{ + return id_; +} + +//Returns entry name +const std::wstring& resource_directory_entry::get_name() const +{ + return name_; +} + +//Returns true, if entry has name +//Returns false, if entry has ID +bool resource_directory_entry::is_named() const +{ + return named_; +} + +//Returns true, if entry includes resource_data_entry +//Returns false, if entry includes resource_directory +bool resource_directory_entry::includes_data() const +{ + return includes_data_; +} + +//Returns resource_directory if entry includes it, otherwise throws an exception +const resource_directory& resource_directory_entry::get_resource_directory() const +{ + if(!ptr_.dir_ || includes_data_) + throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); + + return *ptr_.dir_; +} + +//Returns resource_data_entry if entry includes it, otherwise throws an exception +const resource_data_entry& resource_directory_entry::get_data_entry() const +{ + if(!ptr_.data_ || !includes_data_) + throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); + + return *ptr_.data_; +} + +//Returns resource_directory if entry includes it, otherwise throws an exception +resource_directory& resource_directory_entry::get_resource_directory() +{ + if(!ptr_.dir_ || includes_data_) + throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); + + return *ptr_.dir_; +} + +//Returns resource_data_entry if entry includes it, otherwise throws an exception +resource_data_entry& resource_directory_entry::get_data_entry() +{ + if(!ptr_.data_ || !includes_data_) + throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); + + return *ptr_.data_; +} + +//Sets entry name +void resource_directory_entry::set_name(const std::wstring& name) +{ + name_ = name; + named_ = true; + id_ = 0; +} + +//Sets entry ID +void resource_directory_entry::set_id(uint32_t id) +{ + id_ = id; + named_ = false; + name_.clear(); +} + +//Adds resource_data_entry +void resource_directory_entry::add_data_entry(const resource_data_entry& entry) +{ + release(); + ptr_.data_ = new resource_data_entry(entry); + includes_data_ = true; +} + +//Adds resource_directory +void resource_directory_entry::add_resource_directory(const resource_directory& dir) +{ + release(); + ptr_.dir_ = new resource_directory(dir); + includes_data_ = false; +} + +//Default constructor +resource_directory::resource_directory() + :characteristics_(0), + timestamp_(0), + major_version_(0), minor_version_(0), + number_of_named_entries_(0), number_of_id_entries_(0) +{} + +//Constructor from data +resource_directory::resource_directory(const image_resource_directory& dir) + :characteristics_(dir.Characteristics), + timestamp_(dir.TimeDateStamp), + major_version_(dir.MajorVersion), minor_version_(dir.MinorVersion), + number_of_named_entries_(0), number_of_id_entries_(0) //Set to zero here, calculate on add +{} + +//Returns characteristics of directory +uint32_t resource_directory::get_characteristics() const +{ + return characteristics_; +} + +//Returns date and time stamp of directory +uint32_t resource_directory::get_timestamp() const +{ + return timestamp_; +} + +//Returns major version of directory +uint16_t resource_directory::get_major_version() const +{ + return major_version_; +} + +//Returns minor version of directory +uint16_t resource_directory::get_minor_version() const +{ + return minor_version_; +} + +//Returns number of named entries +uint32_t resource_directory::get_number_of_named_entries() const +{ + return number_of_named_entries_; +} + +//Returns number of ID entries +uint32_t resource_directory::get_number_of_id_entries() const +{ + return number_of_id_entries_; +} + +//Returns resource_directory_entry array +const resource_directory::entry_list& resource_directory::get_entry_list() const +{ + return entries_; +} + +//Returns resource_directory_entry array +resource_directory::entry_list& resource_directory::get_entry_list() +{ + return entries_; +} + +//Adds resource_directory_entry +void resource_directory::add_resource_directory_entry(const resource_directory_entry& entry) +{ + entries_.push_back(entry); + if(entry.is_named()) + ++number_of_named_entries_; + else + ++number_of_id_entries_; +} + +//Clears resource_directory_entry array +void resource_directory::clear_resource_directory_entry_list() +{ + entries_.clear(); + number_of_named_entries_ = 0; + number_of_id_entries_ = 0; +} + +//Sets characteristics of directory +void resource_directory::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets date and time stamp of directory +void resource_directory::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Sets number of named entries +void resource_directory::set_number_of_named_entries(uint32_t number) +{ + number_of_named_entries_ = number; +} + +//Sets number of ID entries +void resource_directory::set_number_of_id_entries(uint32_t number) +{ + number_of_id_entries_ = number; +} + +//Sets major version of directory +void resource_directory::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version of directory +void resource_directory::get_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Processes resource directory +const resource_directory process_resource_directory(const pe_base& pe, uint32_t res_rva, uint32_t offset_to_directory, std::set<uint32_t>& processed) +{ + resource_directory ret; + + //Check for resource loops + if(!processed.insert(offset_to_directory).second) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + if(!pe_utils::is_sum_safe(res_rva, offset_to_directory)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //Get root IMAGE_RESOURCE_DIRECTORY + image_resource_directory directory = pe.section_data_from_rva<image_resource_directory>(res_rva + offset_to_directory, section_data_virtual, true); + + ret = resource_directory(directory); + + //Check DWORDs for possible overflows + if(!pe_utils::is_sum_safe(directory.NumberOfIdEntries, directory.NumberOfNamedEntries) + || directory.NumberOfIdEntries + directory.NumberOfNamedEntries >= pe_utils::max_dword / sizeof(image_resource_directory_entry) + sizeof(image_resource_directory)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + if(!pe_utils::is_sum_safe(offset_to_directory, sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry)) + || !pe_utils::is_sum_safe(res_rva, offset_to_directory + sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry))) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + for(unsigned long i = 0; i != static_cast<unsigned long>(directory.NumberOfIdEntries) + directory.NumberOfNamedEntries; ++i) + { + //Read directory entries one by one + image_resource_directory_entry dir_entry = pe.section_data_from_rva<image_resource_directory_entry>( + res_rva + sizeof(image_resource_directory) + i * sizeof(image_resource_directory_entry) + offset_to_directory, section_data_virtual, true); + + //Create directory entry structure + resource_directory_entry entry; + + //If directory is named + if(dir_entry.NameIsString) + { + if(!pe_utils::is_sum_safe(res_rva + sizeof(uint16_t) /* safe */, dir_entry.NameOffset)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //get directory name length + uint16_t directory_name_length = pe.section_data_from_rva<uint16_t>(res_rva + dir_entry.NameOffset, section_data_virtual, true); + + //Check name length + if(pe.section_data_length_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true) + < directory_name_length) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + +#ifdef PE_BLISS_WINDOWS + //Set entry UNICODE name + entry.set_name(std::wstring( + reinterpret_cast<const wchar_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), + directory_name_length)); +#else + //Set entry UNICODE name + entry.set_name(pe_utils::from_ucs2(u16string( + reinterpret_cast<const unicode16_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), + directory_name_length))); +#endif + } + else + { + //Else - set directory ID + entry.set_id(dir_entry.Id); + } + + //If directory entry has another resource directory + if(dir_entry.DataIsDirectory) + { + entry.add_resource_directory(process_resource_directory(pe, res_rva, dir_entry.OffsetToDirectory, processed)); + } + else + { + //If directory entry has data + image_resource_data_entry data_entry = pe.section_data_from_rva<image_resource_data_entry>( + res_rva + dir_entry.OffsetToData, section_data_virtual, true); + + //Check byte count that stated by data entry + if(pe.section_data_length_from_rva(data_entry.OffsetToData, data_entry.OffsetToData, section_data_virtual, true) < data_entry.Size) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //Add data entry to directory entry + entry.add_data_entry(resource_data_entry( + std::string(pe.section_data_from_rva(data_entry.OffsetToData, section_data_virtual, true), data_entry.Size), + data_entry.CodePage)); + } + + //Save directory entry + ret.add_resource_directory_entry(entry); + } + + //Return resource directory + return ret; +} + +//Helper function to calculate needed space for resource data +void calculate_resource_data_space(const resource_directory& root, uint32_t aligned_offset_from_section_start, uint32_t& needed_size_for_structures, uint32_t& needed_size_for_strings) +{ + needed_size_for_structures += sizeof(image_resource_directory); + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + needed_size_for_structures += sizeof(image_resource_directory_entry); + + if((*it).is_named()) + needed_size_for_strings += static_cast<uint32_t>(((*it).get_name().length() + 1) * 2 /* unicode */ + sizeof(uint16_t) /* for string length */); + + if(!(*it).includes_data()) + calculate_resource_data_space((*it).get_resource_directory(), aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); + } +} + +//Helper function to calculate needed space for resource data +void calculate_resource_data_space(const resource_directory& root, uint32_t needed_size_for_structures, uint32_t needed_size_for_strings, uint32_t& needed_size_for_data, uint32_t& current_data_pos) +{ + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + if((*it).includes_data()) + { + uint32_t data_size = static_cast<uint32_t>((*it).get_data_entry().get_data().length() + + sizeof(image_resource_data_entry) + + (pe_utils::align_up(current_data_pos, sizeof(uint32_t)) - current_data_pos) /* alignment */); + needed_size_for_data += data_size; + current_data_pos += data_size; + } + else + { + calculate_resource_data_space((*it).get_resource_directory(), needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); + } + } +} + +//Helper: sorts resource directory entries +struct entry_sorter +{ +public: + bool operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const; +}; + +//Helper function to rebuild resource directory +void rebuild_resource_directory(pe_base& pe, section& resource_section, resource_directory& root, uint32_t& current_structures_pos, uint32_t& current_data_pos, uint32_t& current_strings_pos, uint32_t offset_from_section_start) +{ + //Create resource directory + image_resource_directory dir = {0}; + dir.Characteristics = root.get_characteristics(); + dir.MajorVersion = root.get_major_version(); + dir.MinorVersion = root.get_minor_version(); + dir.TimeDateStamp = root.get_timestamp(); + + { + resource_directory::entry_list& entries = root.get_entry_list(); + std::sort(entries.begin(), entries.end(), entry_sorter()); + } + + //Calculate number of named and ID entries + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + if((*it).is_named()) + ++dir.NumberOfNamedEntries; + else + ++dir.NumberOfIdEntries; + } + + std::string& raw_data = resource_section.get_raw_data(); + + //Save resource directory + memcpy(&raw_data[current_structures_pos], &dir, sizeof(dir)); + current_structures_pos += sizeof(dir); + + uint32_t this_current_structures_pos = current_structures_pos; + + current_structures_pos += sizeof(image_resource_directory_entry) * (dir.NumberOfNamedEntries + dir.NumberOfIdEntries); + + //Create all resource directory entries + for(resource_directory::entry_list::iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + image_resource_directory_entry entry; + if((*it).is_named()) + { + entry.Name = 0x80000000 | (current_strings_pos - offset_from_section_start); + uint16_t unicode_length = static_cast<uint16_t>((*it).get_name().length()); + memcpy(&raw_data[current_strings_pos], &unicode_length, sizeof(unicode_length)); + current_strings_pos += sizeof(unicode_length); + +#ifdef PE_BLISS_WINDOWS + memcpy(&raw_data[current_strings_pos], (*it).get_name().c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); +#else + { + u16string str(pe_utils::to_ucs2((*it).get_name())); + memcpy(&raw_data[current_strings_pos], str.c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); + } +#endif + + current_strings_pos += static_cast<unsigned long>((*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); + } + else + { + entry.Name = (*it).get_id(); + } + + if((*it).includes_data()) + { + current_data_pos = pe_utils::align_up(current_data_pos, sizeof(uint32_t)); + image_resource_data_entry data_entry = {0}; + data_entry.CodePage = (*it).get_data_entry().get_codepage(); + data_entry.Size = static_cast<uint32_t>((*it).get_data_entry().get_data().length()); + data_entry.OffsetToData = pe.rva_from_section_offset(resource_section, current_data_pos + sizeof(data_entry)); + + entry.OffsetToData = current_data_pos - offset_from_section_start; + + memcpy(&raw_data[current_data_pos], &data_entry, sizeof(data_entry)); + current_data_pos += sizeof(data_entry); + + memcpy(&raw_data[current_data_pos], (*it).get_data_entry().get_data().data(), data_entry.Size); + current_data_pos += data_entry.Size; + + memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); + this_current_structures_pos += sizeof(entry); + } + else + { + entry.OffsetToData = 0x80000000 | (current_structures_pos - offset_from_section_start); + + memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); + this_current_structures_pos += sizeof(entry); + + rebuild_resource_directory(pe, resource_section, (*it).get_resource_directory(), current_structures_pos, current_data_pos, current_strings_pos, offset_from_section_start); + } + } +} + +//Helper function to rebuild resource directory +bool entry_sorter::operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const +{ + if(entry1.is_named() && entry2.is_named()) + return entry1.get_name() < entry2.get_name(); + else if(!entry1.is_named() && !entry2.is_named()) + return entry1.get_id() < entry2.get_id(); + else + return entry1.is_named(); +} + +//Resources rebuilder +//resource_directory - root resource directory +//resources_section - section where resource directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from resources_section raw data start +//resource_directory is non-constant, because it will be sorted +//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers +//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped +//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used +const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that resources_section is attached to this PE image + if(!pe.section_attached(resources_section)) + throw pe_exception("Resource section must be attached to PE file", pe_exception::section_is_not_attached); + + //Check resource directory correctness + if(info.get_entry_list().empty()) + throw pe_exception("Empty resource directory", pe_exception::incorrect_resource_directory); + + uint32_t aligned_offset_from_section_start = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + uint32_t needed_size_for_structures = aligned_offset_from_section_start - offset_from_section_start; //Calculate needed size for resource tables and data + uint32_t needed_size_for_strings = 0; + uint32_t needed_size_for_data = 0; + + calculate_resource_data_space(info, aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); + + { + uint32_t current_data_pos = aligned_offset_from_section_start + needed_size_for_structures + needed_size_for_strings; + calculate_resource_data_space(info, needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); + } + + uint32_t needed_size = needed_size_for_structures + needed_size_for_strings + needed_size_for_data; + + //Check if resources_section is last one. If it's not, check if there's enough place for resource data + if(&resources_section != &*(pe.get_image_sections().end() - 1) && + (resources_section.empty() || pe_utils::align_up(resources_section.get_size_of_raw_data(), pe.get_file_alignment()) + < needed_size + aligned_offset_from_section_start)) + throw pe_exception("Insufficient space for resource directory", pe_exception::insufficient_space); + + std::string& raw_data = resources_section.get_raw_data(); + + //This will be done only if resources_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + aligned_offset_from_section_start) + raw_data.resize(needed_size + aligned_offset_from_section_start); //Expand section raw data + + uint32_t current_structures_pos = aligned_offset_from_section_start; + uint32_t current_strings_pos = current_structures_pos + needed_size_for_structures; + uint32_t current_data_pos = current_strings_pos + needed_size_for_strings; + rebuild_resource_directory(pe, resources_section, info, current_structures_pos, current_data_pos, current_strings_pos, aligned_offset_from_section_start); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(resources_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(resources_section, aligned_offset_from_section_start), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_resource, ret.get_rva()); + pe.set_directory_size(image_directory_entry_resource, ret.get_size()); + } + + return ret; +} + +//Returns resources from PE file +const resource_directory get_resources(const pe_base& pe) +{ + resource_directory ret; + + if(!pe.has_resources()) + return ret; + + //Get resource directory RVA + uint32_t res_rva = pe.get_directory_rva(image_directory_entry_resource); + + //Store already processed directories to avoid resource loops + std::set<uint32_t> processed; + + //Process all directories (recursion) + ret = process_resource_directory(pe, res_rva, 0, processed); + + return ret; +} + +//Finds resource_directory_entry by ID +resource_directory::id_entry_finder::id_entry_finder(uint32_t id) + :id_(id) +{} + +bool resource_directory::id_entry_finder::operator()(const resource_directory_entry& entry) const +{ + return !entry.is_named() && entry.get_id() == id_; +} + +//Finds resource_directory_entry by name +resource_directory::name_entry_finder::name_entry_finder(const std::wstring& name) + :name_(name) +{} + +bool resource_directory::name_entry_finder::operator()(const resource_directory_entry& entry) const +{ + return entry.is_named() && entry.get_name() == name_; +} + +//Finds resource_directory_entry by name or ID (universal) +resource_directory::entry_finder::entry_finder(const std::wstring& name) + :name_(name), named_(true) +{} + +resource_directory::entry_finder::entry_finder(uint32_t id) + :id_(id), named_(false) +{} + +bool resource_directory::entry_finder::operator()(const resource_directory_entry& entry) const +{ + if(named_) + return entry.is_named() && entry.get_name() == name_; + else + return !entry.is_named() && entry.get_id() == id_; +} + +//Returns resource_directory_entry by ID. If not found - throws an exception +const resource_directory_entry& resource_directory::entry_by_id(uint32_t id) const +{ + entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), id_entry_finder(id)); + if(i == entries_.end()) + throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); + + return *i; +} + +//Returns resource_directory_entry by name. If not found - throws an exception +const resource_directory_entry& resource_directory::entry_by_name(const std::wstring& name) const +{ + entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), name_entry_finder(name)); + if(i == entries_.end()) + throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); + + return *i; +} +} diff --git a/pe_lib/pe_resources.h b/pe_lib/pe_resources.h new file mode 100644 index 0000000..934e741 --- /dev/null +++ b/pe_lib/pe_resources.h @@ -0,0 +1,224 @@ +#pragma once +#include <vector> +#include <string> +#include <set> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing resource data entry +class resource_data_entry +{ +public: + //Default constructor + resource_data_entry(); + //Constructor from data + resource_data_entry(const std::string& data, uint32_t codepage); + + //Returns resource data codepage + uint32_t get_codepage() const; + //Returns resource data + const std::string& get_data() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Sets resource data codepage + void set_codepage(uint32_t codepage); + //Sets resource data + void set_data(const std::string& data); + +private: + uint32_t codepage_; //Resource data codepage + std::string data_; //Resource data +}; + +//Forward declaration +class resource_directory; + +//Class representing resource directory entry +class resource_directory_entry +{ +public: + //Default constructor + resource_directory_entry(); + //Copy constructor + resource_directory_entry(const resource_directory_entry& other); + //Copy assignment operator + resource_directory_entry& operator=(const resource_directory_entry& other); + + //Returns entry ID + uint32_t get_id() const; + //Returns entry name + const std::wstring& get_name() const; + //Returns true, if entry has name + //Returns false, if entry has ID + bool is_named() const; + + //Returns true, if entry includes resource_data_entry + //Returns false, if entry includes resource_directory + bool includes_data() const; + //Returns resource_directory if entry includes it, otherwise throws an exception + const resource_directory& get_resource_directory() const; + //Returns resource_data_entry if entry includes it, otherwise throws an exception + const resource_data_entry& get_data_entry() const; + + //Destructor + ~resource_directory_entry(); + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Sets entry name + void set_name(const std::wstring& name); + //Sets entry ID + void set_id(uint32_t id); + + //Returns resource_directory if entry includes it, otherwise throws an exception + resource_directory& get_resource_directory(); + //Returns resource_data_entry if entry includes it, otherwise throws an exception + resource_data_entry& get_data_entry(); + + //Adds resource_data_entry + void add_data_entry(const resource_data_entry& entry); + //Adds resource_directory + void add_resource_directory(const resource_directory& dir); + +private: + //Destroys included data + void release(); + +private: + uint32_t id_; + std::wstring name_; + + union includes + { + //Default constructor + includes(); + + //We use pointers, we're doing manual copying here + class resource_data_entry* data_; + class resource_directory* dir_; //We use pointer, because structs include each other + }; + + includes ptr_; + + bool includes_data_, named_; +}; + +//Class representing resource directory +class resource_directory +{ +public: + typedef std::vector<resource_directory_entry> entry_list; + +public: + //Default constructor + resource_directory(); + //Constructor from data + explicit resource_directory(const pe_win::image_resource_directory& dir); + + //Returns characteristics of directory + uint32_t get_characteristics() const; + //Returns date and time stamp of directory + uint32_t get_timestamp() const; + //Returns number of named entries + uint32_t get_number_of_named_entries() const; + //Returns number of ID entries + uint32_t get_number_of_id_entries() const; + //Returns major version of directory + uint16_t get_major_version() const; + //Returns minor version of directory + uint16_t get_minor_version() const; + //Returns resource_directory_entry array + const entry_list& get_entry_list() const; + //Returns resource_directory_entry by ID. If not found - throws an exception + const resource_directory_entry& entry_by_id(uint32_t id) const; + //Returns resource_directory_entry by name. If not found - throws an exception + const resource_directory_entry& entry_by_name(const std::wstring& name) const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Adds resource_directory_entry + void add_resource_directory_entry(const resource_directory_entry& entry); + //Clears resource_directory_entry array + void clear_resource_directory_entry_list(); + + //Sets characteristics of directory + void set_characteristics(uint32_t characteristics); + //Sets date and time stamp of directory + void set_timestamp(uint32_t timestamp); + //Sets number of named entries + void set_number_of_named_entries(uint32_t number); + //Sets number of ID entries + void set_number_of_id_entries(uint32_t number); + //Sets major version of directory + void set_major_version(uint16_t major_version); + //Sets minor version of directory + void get_minor_version(uint16_t minor_version); + + //Returns resource_directory_entry array + entry_list& get_entry_list(); + +private: + uint32_t characteristics_; + uint32_t timestamp_; + uint16_t major_version_, minor_version_; + uint32_t number_of_named_entries_, number_of_id_entries_; + entry_list entries_; + +public: //Finder helpers + //Finds resource_directory_entry by ID + struct id_entry_finder + { + public: + explicit id_entry_finder(uint32_t id); + bool operator()(const resource_directory_entry& entry) const; + + private: + uint32_t id_; + }; + + //Finds resource_directory_entry by name + struct name_entry_finder + { + public: + explicit name_entry_finder(const std::wstring& name); + bool operator()(const resource_directory_entry& entry) const; + + private: + std::wstring name_; + }; + + //Finds resource_directory_entry by name or ID (universal) + struct entry_finder + { + public: + explicit entry_finder(const std::wstring& name); + explicit entry_finder(uint32_t id); + bool operator()(const resource_directory_entry& entry) const; + + private: + std::wstring name_; + uint32_t id_; + bool named_; + }; +}; + +//Returns resources (root resource_directory) from PE file +const resource_directory get_resources(const pe_base& pe); + +//Resources rebuilder +//resource_directory - root resource directory +//resources_section - section where resource directory will be placed (must be attached to PE image) +//resource_directory is non-constant, because it will be sorted +//offset_from_section_start - offset from resources_section raw data start +//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers +//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped +//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used +const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/pe_lib/pe_rich_data.cpp b/pe_lib/pe_rich_data.cpp new file mode 100644 index 0000000..94e5593 --- /dev/null +++ b/pe_lib/pe_rich_data.cpp @@ -0,0 +1,131 @@ +#include "pe_rich_data.h" + +namespace pe_bliss +{ +//STUB OVERLAY +//Default constructor +rich_data::rich_data() + :number_(0), version_(0), times_(0) +{} + +//Who knows, what these fields mean... +uint32_t rich_data::get_number() const +{ + return number_; +} + +uint32_t rich_data::get_version() const +{ + return version_; +} + +uint32_t rich_data::get_times() const +{ + return times_; +} + +void rich_data::set_number(uint32_t number) +{ + number_ = number; +} + +void rich_data::set_version(uint32_t version) +{ + version_ = version; +} + +void rich_data::set_times(uint32_t times) +{ + times_ = times; +} + +//Returns MSVC rich data +const rich_data_list get_rich_data(const pe_base& pe) +{ + //Returned value + rich_data_list ret; + + const std::string& rich_overlay = pe.get_stub_overlay(); + + //If there's no rich overlay, return empty vector + if(rich_overlay.size() < sizeof(uint32_t)) + return ret; + + //True if rich data was found + bool found = false; + + //Rich overlay ID ("Rich" word) + static const uint32_t rich_overlay_id = 0x68636952; + + //Search for rich data overlay ID + const char* begin = &rich_overlay[0]; + const char* end = begin + rich_overlay.length(); + for(; begin != end; ++begin) + { + if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) + { + found = true; //We've found it! + break; + } + } + + //If we found it + if(found) + { + //Check remaining length + if(static_cast<size_t>(end - begin) < sizeof(uint32_t)) + return ret; + + //The XOR key is after "Rich" word, we should get it + uint32_t xorkey = *reinterpret_cast<const uint32_t*>(begin + sizeof(uint32_t)); + + //True if rich data was found + found = false; + + //Second search for signature "DanS" + begin = &rich_overlay[0]; + for(; begin != end; ++begin) + { + if((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) == 0x536e6144) //"DanS" + { + found = true; + break; + } + } + + //If second signature is found + if(found) + { + begin += sizeof(uint32_t) * 3; + //List all rich data structures + while(begin < end) + { + begin += sizeof(uint32_t); + if(begin >= end) + break; + + //Check for rich overlay data end ("Rich" word reached) + if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) + break; + + //Create rich_data structure + rich_data data; + data.set_number((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) >> 16); + data.set_version((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) & 0xFFFF); + + begin += sizeof(uint32_t); + if(begin >= end) + break; + + data.set_times(*reinterpret_cast<const uint32_t*>(begin) ^ xorkey); + + //Save rich data structure + ret.push_back(data); + } + } + } + + //Return rich data structures list + return ret; +} +} diff --git a/pe_lib/pe_rich_data.h b/pe_lib/pe_rich_data.h new file mode 100644 index 0000000..34f4a3c --- /dev/null +++ b/pe_lib/pe_rich_data.h @@ -0,0 +1,37 @@ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Rich data overlay class of Microsoft Visual Studio +class rich_data +{ +public: + //Default constructor + rich_data(); + +public: //Getters + //Who knows, what these fields mean... + uint32_t get_number() const; + uint32_t get_version() const; + uint32_t get_times() const; + +public: //Setters, used by PE library only + void set_number(uint32_t number); + void set_version(uint32_t version); + void set_times(uint32_t times); + +private: + uint32_t number_; + uint32_t version_; + uint32_t times_; +}; + +//Rich data list typedef +typedef std::vector<rich_data> rich_data_list; + +//Returns a vector with rich data (stub overlay) +const rich_data_list get_rich_data(const pe_base& pe); +} diff --git a/pe_lib/pe_section.cpp b/pe_lib/pe_section.cpp new file mode 100644 index 0000000..caa2050 --- /dev/null +++ b/pe_lib/pe_section.cpp @@ -0,0 +1,281 @@ +#include <string.h> +#include "utils.h" +#include "pe_section.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Section structure default constructor +section::section() + :old_size_(static_cast<size_t>(-1)) +{ + memset(&header_, 0, sizeof(image_section_header)); +} + +//Sets the name of section (8 characters maximum) +void section::set_name(const std::string& name) +{ + memset(header_.Name, 0, sizeof(header_.Name)); + memcpy(header_.Name, name.c_str(), std::min<size_t>(name.length(), sizeof(header_.Name))); +} + +//Returns section name +const std::string section::get_name() const +{ + char buf[9] = {0}; + memcpy(buf, header_.Name, 8); + return std::string(buf); +} + +//Set flag (attribute) of section +section& section::set_flag(uint32_t flag, bool setflag) +{ + if(setflag) + header_.Characteristics |= flag; + else + header_.Characteristics &= ~flag; + + return *this; +} + +//Sets "readable" attribute of section +section& section::readable(bool readable) +{ + return set_flag(image_scn_mem_read, readable); +} + +//Sets "writeable" attribute of section +section& section::writeable(bool writeable) +{ + return set_flag(image_scn_mem_write, writeable); +} + +//Sets "executable" attribute of section +section& section::executable(bool executable) +{ + return set_flag(image_scn_mem_execute, executable); +} + +//Sets "shared" attribute of section +section& section::shared(bool shared) +{ + return set_flag(image_scn_mem_shared, shared); +} + +//Sets "discardable" attribute of section +section& section::discardable(bool discardable) +{ + return set_flag(image_scn_mem_discardable, discardable); +} + +//Returns true if section is readable +bool section::readable() const +{ + return (header_.Characteristics & image_scn_mem_read) != 0; +} + +//Returns true if section is writeable +bool section::writeable() const +{ + return (header_.Characteristics & image_scn_mem_write) != 0; +} + +//Returns true if section is executable +bool section::executable() const +{ + return (header_.Characteristics & image_scn_mem_execute) != 0; +} + +bool section::shared() const +{ + return (header_.Characteristics & image_scn_mem_shared) != 0; +} + +bool section::discardable() const +{ + return (header_.Characteristics & image_scn_mem_discardable) != 0; +} + +//Returns true if section has no RAW data +bool section::empty() const +{ + if(old_size_ != static_cast<size_t>(-1)) //If virtual memory is mapped, check raw data length (old_size_) + return old_size_ == 0; + else + return raw_data_.empty(); +} + +//Returns raw section data from file image +std::string& section::get_raw_data() +{ + unmap_virtual(); + return raw_data_; +} + +//Sets raw section data from file image +void section::set_raw_data(const std::string& data) +{ + old_size_ = static_cast<size_t>(-1); + raw_data_ = data; +} + +//Returns raw section data from file image +const std::string& section::get_raw_data() const +{ + unmap_virtual(); + return raw_data_; +} + +//Returns mapped virtual section data +const std::string& section::get_virtual_data(uint32_t section_alignment) const +{ + map_virtual(section_alignment); + return raw_data_; +} + +//Returns mapped virtual section data +std::string& section::get_virtual_data(uint32_t section_alignment) +{ + map_virtual(section_alignment); + return raw_data_; +} + +//Maps virtual section data +void section::map_virtual(uint32_t section_alignment) const +{ + uint32_t aligned_virtual_size = get_aligned_virtual_size(section_alignment); + if(old_size_ == static_cast<size_t>(-1) && aligned_virtual_size && aligned_virtual_size > raw_data_.length()) + { + old_size_ = raw_data_.length(); + raw_data_.resize(aligned_virtual_size, 0); + } +} + +//Unmaps virtual section data +void section::unmap_virtual() const +{ + if(old_size_ != static_cast<size_t>(-1)) + { + raw_data_.resize(old_size_, 0); + old_size_ = static_cast<size_t>(-1); + } +} + +//Returns section virtual size +uint32_t section::get_virtual_size() const +{ + return header_.Misc.VirtualSize; +} + +//Returns section virtual address +uint32_t section::get_virtual_address() const +{ + return header_.VirtualAddress; +} + +//Returns size of section raw data +uint32_t section::get_size_of_raw_data() const +{ + return header_.SizeOfRawData; +} + +//Returns pointer to raw section data in PE file +uint32_t section::get_pointer_to_raw_data() const +{ + return header_.PointerToRawData; +} + +//Returns section characteristics +uint32_t section::get_characteristics() const +{ + return header_.Characteristics; +} + +//Returns raw image section header +const pe_win::image_section_header& section::get_raw_header() const +{ + return header_; +} + +//Returns raw image section header +pe_win::image_section_header& section::get_raw_header() +{ + return header_; +} + +//Calculates aligned virtual section size +uint32_t section::get_aligned_virtual_size(uint32_t section_alignment) const +{ + if(get_size_of_raw_data()) + { + if(!get_virtual_size()) + { + //If section virtual size is zero + //Set aligned virtual size of section as aligned raw size + return pe_utils::align_up(get_size_of_raw_data(), section_alignment); + } + } + + return pe_utils::align_up(get_virtual_size(), section_alignment); +} + +//Calculates aligned raw section size +uint32_t section::get_aligned_raw_size(uint32_t file_alignment) const +{ + if(get_size_of_raw_data()) + return pe_utils::align_up(get_size_of_raw_data(), file_alignment); + else + return 0; +} + +//Sets size of raw section data +void section::set_size_of_raw_data(uint32_t size_of_raw_data) +{ + header_.SizeOfRawData = size_of_raw_data; +} + +//Sets pointer to section raw data +void section::set_pointer_to_raw_data(uint32_t pointer_to_raw_data) +{ + header_.PointerToRawData = pointer_to_raw_data; +} + +//Sets section characteristics +void section::set_characteristics(uint32_t characteristics) +{ + header_.Characteristics = characteristics; +} + +//Sets section virtual size +void section::set_virtual_size(uint32_t virtual_size) +{ + header_.Misc.VirtualSize = virtual_size; +} + +//Sets section virtual address +void section::set_virtual_address(uint32_t virtual_address) +{ + header_.VirtualAddress = virtual_address; +} + +//Section by file offset finder helper (4gb max) +section_by_raw_offset::section_by_raw_offset(uint32_t offset) + :offset_(offset) +{} + +bool section_by_raw_offset::operator()(const section& s) const +{ + return (s.get_pointer_to_raw_data() <= offset_) + && (s.get_pointer_to_raw_data() + s.get_size_of_raw_data() > offset_); +} + +section_ptr_finder::section_ptr_finder(const section& s) + :s_(s) +{} + +bool section_ptr_finder::operator()(const section& s) const +{ + return &s == &s_; +} +} diff --git a/pe_lib/pe_section.h b/pe_lib/pe_section.h new file mode 100644 index 0000000..e9706c7 --- /dev/null +++ b/pe_lib/pe_section.h @@ -0,0 +1,137 @@ +#pragma once +#include <string> +#include <vector> +#include "pe_structures.h" + +namespace pe_bliss +{ +//Enumeration of section data types, used in functions below +enum section_data_type +{ + section_data_raw, + section_data_virtual +}; + +//Class representing image section +class section +{ +public: + //Default constructor + section(); + + //Sets the name of section (stripped to 8 characters) + void set_name(const std::string& name); + + //Returns the name of section + const std::string get_name() const; + + //Changes attributes of section + section& readable(bool readable); + section& writeable(bool writeable); + section& executable(bool executable); + section& shared(bool shared); + section& discardable(bool discardable); + + //Returns attributes of section + bool readable() const; + bool writeable() const; + bool executable() const; + bool shared() const; + bool discardable() const; + + //Returns true if section has no RAW data + bool empty() const; + + //Returns raw section data from file image + std::string& get_raw_data(); + //Returns raw section data from file image + const std::string& get_raw_data() const; + //Returns mapped virtual section data + const std::string& get_virtual_data(uint32_t section_alignment) const; + //Returns mapped virtual section data + std::string& get_virtual_data(uint32_t section_alignment); + +public: //Header getters + //Returns section virtual size + uint32_t get_virtual_size() const; + //Returns section virtual address (RVA) + uint32_t get_virtual_address() const; + //Returns size of section raw data + uint32_t get_size_of_raw_data() const; + //Returns pointer to raw section data in PE file + uint32_t get_pointer_to_raw_data() const; + //Returns section characteristics + uint32_t get_characteristics() const; + + //Returns raw image section header + const pe_win::image_section_header& get_raw_header() const; + +public: //Aligned sizes calculation + //Calculates aligned virtual section size + uint32_t get_aligned_virtual_size(uint32_t section_alignment) const; + //Calculates aligned raw section size + uint32_t get_aligned_raw_size(uint32_t file_alignment) const; + +public: //Setters + //Sets size of raw section data + void set_size_of_raw_data(uint32_t size_of_raw_data); + //Sets pointer to section raw data + void set_pointer_to_raw_data(uint32_t pointer_to_raw_data); + //Sets section characteristics + void set_characteristics(uint32_t characteristics); + //Sets raw section data from file image + void set_raw_data(const std::string& data); + +public: //Setters, be careful + //Sets section virtual size (doesn't set internal aligned virtual size, changes only header value) + //Better use pe_base::set_section_virtual_size + void set_virtual_size(uint32_t virtual_size); + //Sets section virtual address + void set_virtual_address(uint32_t virtual_address); + //Returns raw image section header + pe_win::image_section_header& get_raw_header(); + +private: + //Section header + pe_win::image_section_header header_; + + //Maps virtual section data + void map_virtual(uint32_t section_alignment) const; + + //Unmaps virtual section data + void unmap_virtual() const; + + //Set flag (attribute) of section + section& set_flag(uint32_t flag, bool setflag); + + //Old size of section (stored after mapping of virtual section memory) + mutable std::size_t old_size_; + + //Section raw/virtual data + mutable std::string raw_data_; +}; + +//Section by file offset finder helper (4gb max) +struct section_by_raw_offset +{ +public: + explicit section_by_raw_offset(uint32_t offset); + bool operator()(const section& s) const; + +private: + uint32_t offset_; +}; + +//Helper: finder of section* in sections list +struct section_ptr_finder +{ +public: + explicit section_ptr_finder(const section& s); + bool operator()(const section& s) const; + +private: + const section& s_; +}; + +typedef std::vector<section> section_list; +} diff --git a/pe_lib/pe_structures.h b/pe_lib/pe_structures.h index 4508341..c27dcb8 100644 --- a/pe_lib/pe_structures.h +++ b/pe_lib/pe_structures.h @@ -8,6 +8,13 @@ namespace pe_bliss { +//Enumeration of PE types +enum pe_type +{ + pe_type_32, + pe_type_64 +}; + namespace pe_win { const uint32_t image_numberof_directory_entries = 16; @@ -555,7 +562,7 @@ struct cursor_group //Structure representing CURSOR directory entry inside CURSOR file struct cursordirentry { - uint8_t Width; //todo [comment] Set to CURSOR_GROUP::Height/2. + uint8_t Width; //Set to CURSOR_GROUP::Height/2. uint8_t Height; uint8_t ColorCount; uint8_t Reserved; diff --git a/pe_lib/pe_tls.cpp b/pe_lib/pe_tls.cpp new file mode 100644 index 0000000..c10e34b --- /dev/null +++ b/pe_lib/pe_tls.cpp @@ -0,0 +1,375 @@ +#include <string.h> +#include "pe_tls.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//TLS +//Default constructor +tls_info::tls_info() + :start_rva_(0), end_rva_(0), index_rva_(0), callbacks_rva_(0), + size_of_zero_fill_(0), characteristics_(0) +{} + +//Returns start RVA of TLS raw data +uint32_t tls_info::get_raw_data_start_rva() const +{ + return start_rva_; +} + +//Returns end RVA of TLS raw data +uint32_t tls_info::get_raw_data_end_rva() const +{ + return end_rva_; +} + +//Returns TLS index RVA +uint32_t tls_info::get_index_rva() const +{ + return index_rva_; +} + +//Returns TLS callbacks RVA +uint32_t tls_info::get_callbacks_rva() const +{ + return callbacks_rva_; +} + +//Returns size of zero fill +uint32_t tls_info::get_size_of_zero_fill() const +{ + return size_of_zero_fill_; +} + +//Returns characteristics +uint32_t tls_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns raw TLS data +const std::string& tls_info::get_raw_data() const +{ + return raw_data_; +} + +//Returns TLS callbacks addresses +const tls_info::tls_callback_list& tls_info::get_tls_callbacks() const +{ + return callbacks_; +} + +//Returns TLS callbacks addresses +tls_info::tls_callback_list& tls_info::get_tls_callbacks() +{ + return callbacks_; +} + +//Adds TLS callback +void tls_info::add_tls_callback(uint32_t rva) +{ + callbacks_.push_back(rva); +} + +//Clears TLS callbacks list +void tls_info::clear_tls_callbacks() +{ + callbacks_.clear(); +} + +//Recalculates end address of raw TLS data +void tls_info::recalc_raw_data_end_rva() +{ + end_rva_ = static_cast<uint32_t>(start_rva_ + raw_data_.length()); +} + +//Sets start RVA of TLS raw data +void tls_info::set_raw_data_start_rva(uint32_t rva) +{ + start_rva_ = rva; +} + +//Sets end RVA of TLS raw data +void tls_info::set_raw_data_end_rva(uint32_t rva) +{ + end_rva_ = rva; +} + +//Sets TLS index RVA +void tls_info::set_index_rva(uint32_t rva) +{ + index_rva_ = rva; +} + +//Sets TLS callbacks RVA +void tls_info::set_callbacks_rva(uint32_t rva) +{ + callbacks_rva_ = rva; +} + +//Sets size of zero fill +void tls_info::set_size_of_zero_fill(uint32_t size) +{ + size_of_zero_fill_ = size; +} + +//Sets characteristics +void tls_info::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets raw TLS data +void tls_info::set_raw_data(const std::string& data) +{ + raw_data_ = data; +} + +//If image does not have TLS, throws an exception +const tls_info get_tls_info(const pe_base& pe) +{ + return pe.get_pe_type() == pe_type_32 + ? get_tls_info_base<pe_types_class_32>(pe) + : get_tls_info_base<pe_types_class_64>(pe); +} + +//TLS Rebuilder +const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section) +{ + return pe.get_pe_type() == pe_type_32 + ? rebuild_tls_base<pe_types_class_32>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section) + : rebuild_tls_base<pe_types_class_64>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section); +} + +//Get TLS info +//If image does not have TLS, throws an exception +template<typename PEClassType> +const tls_info get_tls_info_base(const pe_base& pe) +{ + tls_info ret; + + //If there's no TLS directory, throw an exception + if(!pe.has_tls()) + throw pe_exception("Image does not have TLS directory", pe_exception::directory_does_not_exist); + + //Get TLS directory data + typename PEClassType::TLSStruct tls_directory_data = pe.section_data_from_rva<typename PEClassType::TLSStruct>(pe.get_directory_rva(image_directory_entry_tls), section_data_virtual, true); + + //Check data addresses + if(tls_directory_data.EndAddressOfRawData == tls_directory_data.StartAddressOfRawData) + { + try + { + pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData)); + } + catch(const pe_exception&) + { + //Fix addressess on incorrect conversion + tls_directory_data.EndAddressOfRawData = tls_directory_data.StartAddressOfRawData = 0; + } + } + + if(tls_directory_data.StartAddressOfRawData && + pe.section_data_length_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), + static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true) + < (tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData)) + throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory); + + //Fill TLS info + //VAs are not checked + ret.set_raw_data_start_rva(tls_directory_data.StartAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData)) : 0); + ret.set_raw_data_end_rva(tls_directory_data.EndAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData)) : 0); + ret.set_index_rva(tls_directory_data.AddressOfIndex ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfIndex)) : 0); + ret.set_callbacks_rva(tls_directory_data.AddressOfCallBacks ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks)) : 0); + ret.set_size_of_zero_fill(tls_directory_data.SizeOfZeroFill); + ret.set_characteristics(tls_directory_data.Characteristics); + + if(tls_directory_data.StartAddressOfRawData && tls_directory_data.StartAddressOfRawData != tls_directory_data.EndAddressOfRawData) + { + //Read and save TLS RAW data + ret.set_raw_data(std::string( + pe.section_data_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true), + static_cast<uint32_t>(tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData))); + } + + //If file has TLS callbacks + if(ret.get_callbacks_rva()) + { + //Read callbacks VAs + uint32_t current_tls_callback = 0; + + while(true) + { + //Read TLS callback VA + typename PEClassType::BaseSize va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks + current_tls_callback), section_data_virtual, true); + if(va == 0) + break; + + //Save it + ret.add_tls_callback(pe.va_to_rva(va, false)); + + //Move to next callback VA + current_tls_callback += sizeof(va); + } + } + + return ret; +} + +//Rebuilder of TLS structures +//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place +//If write_tls_data = true, TLS data will be written to its place +//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string +//representing raw data content +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//Note/TODO: TLS Callbacks array is not DWORD-aligned (seems to work on WinXP - Win7) +template<typename PEClassType> +const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that tls_section is attached to this PE image + if(!pe.section_attached(tls_section)) + throw pe_exception("TLS section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t tls_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)); + uint32_t needed_size = sizeof(typename PEClassType::TLSStruct); //Calculate needed size for TLS table + + //Check if tls_section is last one. If it's not, check if there's enough place for TLS data + if(&tls_section != &*(pe.get_image_sections().end() - 1) && + (tls_section.empty() || pe_utils::align_up(tls_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + tls_data_pos)) + throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space); + + //Check raw data positions + if(info.get_raw_data_end_rva() < info.get_raw_data_start_rva() || info.get_index_rva() == 0) + throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory); + + std::string& raw_data = tls_section.get_raw_data(); + + //This will be done only if tls_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + tls_data_pos) + raw_data.resize(needed_size + tls_data_pos); //Expand section raw data + + //Create and fill TLS structure + typename PEClassType::TLSStruct tls_struct = {0}; + + typename PEClassType::BaseSize va; + if(info.get_raw_data_start_rva()) + { + pe.rva_to_va(info.get_raw_data_start_rva(), va); + tls_struct.StartAddressOfRawData = va; + tls_struct.SizeOfZeroFill = info.get_size_of_zero_fill(); + } + + if(info.get_raw_data_end_rva()) + { + pe.rva_to_va(info.get_raw_data_end_rva(), va); + tls_struct.EndAddressOfRawData = va; + } + + pe.rva_to_va(info.get_index_rva(), va); + tls_struct.AddressOfIndex = va; + + if(info.get_callbacks_rva()) + { + pe.rva_to_va(info.get_callbacks_rva(), va); + tls_struct.AddressOfCallBacks = va; + } + + tls_struct.Characteristics = info.get_characteristics(); + + //Save TLS structure + memcpy(&raw_data[tls_data_pos], &tls_struct, sizeof(tls_struct)); + + //If we are asked to rewrite TLS raw data + if(write_tls_data && info.get_raw_data_start_rva() && info.get_raw_data_start_rva() != info.get_raw_data_end_rva()) + { + try + { + //Check if we're going to write TLS raw data to an existing section (not to PE headers) + section& raw_data_section = pe.section_from_rva(info.get_raw_data_start_rva()); + pe.expand_section(raw_data_section, info.get_raw_data_start_rva(), info.get_raw_data_end_rva() - info.get_raw_data_start_rva(), expand == tls_data_expand_raw ? pe_base::expand_section_raw : pe_base::expand_section_virtual); + } + catch(const pe_exception&) + { + //If no section is presented by StartAddressOfRawData, just go to next step + } + + unsigned long write_raw_data_size = info.get_raw_data_end_rva() - info.get_raw_data_start_rva(); + unsigned long available_raw_length = 0; + + //Check if there's enough RAW space to write raw TLS data... + if((available_raw_length = pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_raw, true)) + < info.get_raw_data_end_rva() - info.get_raw_data_start_rva()) + { + //Check if there's enough virtual space for it... + if(pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_virtual, true) + < info.get_raw_data_end_rva() - info.get_raw_data_start_rva()) + throw pe_exception("Insufficient space for TLS raw data", pe_exception::insufficient_space); + else + write_raw_data_size = available_raw_length; //We'll write just a part of full raw data + } + + //Write raw TLS data, if any + if(write_raw_data_size != 0) + memcpy(pe.section_data_from_rva(info.get_raw_data_start_rva(), true), info.get_raw_data().data(), write_raw_data_size); + } + + //If we are asked to rewrite TLS callbacks addresses + if(write_tls_callbacks && info.get_callbacks_rva()) + { + unsigned long needed_callback_size = static_cast<unsigned long>((info.get_tls_callbacks().size() + 1 /* last null element */) * sizeof(typename PEClassType::BaseSize)); + + try + { + //Check if we're going to write TLS callbacks VAs to an existing section (not to PE headers) + section& raw_data_section = pe.section_from_rva(info.get_callbacks_rva()); + pe.expand_section(raw_data_section, info.get_callbacks_rva(), needed_callback_size, pe_base::expand_section_raw); + } + catch(const pe_exception&) + { + //If no section is presented by RVA of callbacks, just go to next step + } + + //Check if there's enough space to write callbacks TLS data... + if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_raw, true) + < needed_callback_size - sizeof(typename PEClassType::BaseSize) /* last zero element can be virtual only */) + throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space); + + if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_virtual, true) + < needed_callback_size /* check here full virtual data length available */) + throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space); + + std::vector<typename PEClassType::BaseSize> callbacks_virtual_addresses; + callbacks_virtual_addresses.reserve(info.get_tls_callbacks().size() + 1 /* last null element */); + + //Convert TLS RVAs to VAs + for(tls_info::tls_callback_list::const_iterator it = info.get_tls_callbacks().begin(); it != info.get_tls_callbacks().end(); ++it) + { + typename PEClassType::BaseSize cb_va = 0; + pe.rva_to_va(*it, cb_va); + callbacks_virtual_addresses.push_back(cb_va); + } + + //Ending null element + callbacks_virtual_addresses.push_back(0); + + //Write callbacks TLS data + memcpy(pe.section_data_from_rva(info.get_callbacks_rva(), true), &callbacks_virtual_addresses[0], needed_callback_size); + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(tls_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(tls_section, tls_data_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_tls, ret.get_rva()); + pe.set_directory_size(image_directory_entry_tls, ret.get_size()); + } + + return ret; +} +} diff --git a/pe_lib/pe_tls.h b/pe_lib/pe_tls.h new file mode 100644 index 0000000..983633e --- /dev/null +++ b/pe_lib/pe_tls.h @@ -0,0 +1,101 @@ +#pragma once +#include <memory> +#include <istream> +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing TLS info +//We use "DWORD" type to represent RVAs, because RVA is +//always 32bit even in PE+ +class tls_info +{ +public: + typedef std::vector<uint32_t> tls_callback_list; + +public: + //Default constructor + tls_info(); + + //Returns start RVA of TLS raw data + uint32_t get_raw_data_start_rva() const; + //Returns end RVA of TLS raw data + uint32_t get_raw_data_end_rva() const; + //Returns TLS index RVA + uint32_t get_index_rva() const; + //Returns TLS callbacks RVA + uint32_t get_callbacks_rva() const; + //Returns size of zero fill + uint32_t get_size_of_zero_fill() const; + //Returns characteristics + uint32_t get_characteristics() const; + //Returns raw TLS data + const std::string& get_raw_data() const; + //Returns TLS callbacks addresses + const tls_callback_list& get_tls_callbacks() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild TLS directory + + //Sets start RVA of TLS raw data + void set_raw_data_start_rva(uint32_t rva); + //Sets end RVA of TLS raw data + void set_raw_data_end_rva(uint32_t rva); + //Sets TLS index RVA + void set_index_rva(uint32_t rva); + //Sets TLS callbacks RVA + void set_callbacks_rva(uint32_t rva); + //Sets size of zero fill + void set_size_of_zero_fill(uint32_t size); + //Sets characteristics + void set_characteristics(uint32_t characteristics); + //Sets raw TLS data + void set_raw_data(const std::string& data); + //Returns TLS callbacks addresses + tls_callback_list& get_tls_callbacks(); + //Adds TLS callback + void add_tls_callback(uint32_t rva); + //Clears TLS callbacks list + void clear_tls_callbacks(); + //Recalculates end address of raw TLS data + void recalc_raw_data_end_rva(); + +private: + uint32_t start_rva_, end_rva_, index_rva_, callbacks_rva_; + uint32_t size_of_zero_fill_, characteristics_; + + //Raw TLS data + std::string raw_data_; + + //TLS callback RVAs + tls_callback_list callbacks_; +}; + +//Represents type of expanding of TLS section containing raw data +//(Works only if you are writing TLS raw data to tls_section and it is the last one in the PE image on the moment of TLS rebuild) +enum tls_data_expand_type +{ + tls_data_expand_raw, //If there is not enough raw space for raw TLS data, it can be expanded + tls_data_expand_virtual //If there is not enough virtual place for raw TLS data, it can be expanded +}; + + +//Get TLS info +//If image does not have TLS, throws an exception +const tls_info get_tls_info(const pe_base& pe); + +template<typename PEClassType> +const tls_info get_tls_info_base(const pe_base& pe); + +//Rebuilder of TLS structures +//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place +//If write_tls_data = true, TLS data will be written to its place +//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string +//representing raw data content +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +template<typename PEClassType> +const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/pe_lib/readme.txt b/pe_lib/readme.txt index 7b05a1e..300fbf8 100644 --- a/pe_lib/readme.txt +++ b/pe_lib/readme.txt @@ -1,4 +1,4 @@ -Îòêðûòàÿ áåñïëàòíàÿ áèáëèîòåêà äëÿ ðàáîòû ñ PE-ôàéëàìè PEBliss. +Îòêðûòàÿ áåñïëàòíàÿ áèáëèîòåêà äëÿ ðàáîòû ñ PE-ôàéëàìè PE Bliss. Áåñïëàòíà ê èñïîëüçîâàíèþ, ìîäèôèêàöèè è ðàñïðîñòðàíåíèþ. Àâòîð: DX (c) DX 2011-2012, kaimi.ru @@ -19,7 +19,7 @@ [+] ×òåíèå è ðåäàêòèðîâàíèå TLS [+] ×òåíèå è ðåäàêòèðîâàíèå êîíôèãóðàöèè îáðàçà (image config) [+] ×òåíèå áàçîâîé èíôîðìàöèè .NET -[+] ×òåíèå èíôîðìàöèè î ïðèâÿçàííîì èìïîðòå +[+] ×òåíèå è ðåäàêòèðîâàíèå èíôîðìàöèè î ïðèâÿçàííîì èìïîðòå [+] ×òåíèå äèðåêòîðèè èñêëþ÷åíèé (òîëüêî PE+) [+] ×òåíèå îòëàäî÷íîé äèðåêòîðèè ñ ðàñøèðåííîé èíôîðìàöèåé [+] Âû÷èñëåíèå ýíòðîïèè diff --git a/pe_lib/resource_bitmap_reader.cpp b/pe_lib/resource_bitmap_reader.cpp new file mode 100644 index 0000000..295b794 --- /dev/null +++ b/pe_lib/resource_bitmap_reader.cpp @@ -0,0 +1,65 @@ +#include <cmath> +#include "resource_bitmap_reader.h" +#include "pe_resource_viewer.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_bitmap_reader::resource_bitmap_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_name(const std::wstring& name, uint32_t index) const +{ + return create_bitmap(res_.get_resource_data_by_name(pe_resource_viewer::resource_bitmap, name, index).get_data()); +} + +//Returns bitmap data by name and language (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_name(uint32_t language, const std::wstring& name) const +{ + return create_bitmap(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_bitmap, name).get_data()); +} + +//Returns bitmap data by ID and language (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_id_lang(uint32_t language, uint32_t id) const +{ + return create_bitmap(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_bitmap, id).get_data()); +} + +//Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_id(uint32_t id, uint32_t index) const +{ + return create_bitmap(res_.get_resource_data_by_id(pe_resource_viewer::resource_bitmap, id, index).get_data()); +} + +//Helper function of creating bitmap header +const std::string resource_bitmap_reader::create_bitmap(const std::string& resource_data) +{ + //Create bitmap file header + bitmapfileheader header = {0}; + header.bfType = 0x4d42; //Signature "BM" + header.bfOffBits = sizeof(bitmapfileheader) + sizeof(bitmapinfoheader); //Offset to bitmap bits + header.bfSize = static_cast<uint32_t>(sizeof(bitmapfileheader) + resource_data.length()); //Size of bitmap + + //Check size of resource data + if(resource_data.length() < sizeof(bitmapinfoheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + { + //Get bitmap info header + const bitmapinfoheader* info = reinterpret_cast<const bitmapinfoheader*>(resource_data.data()); + + //If color table is present, skip it + if(info->biClrUsed != 0) + header.bfOffBits += 4 * info->biClrUsed; //Add this size to offset to bitmap bits + else if(info->biBitCount <= 8) + header.bfOffBits += 4 * static_cast<uint32_t>(std::pow(2.f, info->biBitCount)); //Add this size to offset to bitmap bits + } + + //Return final bitmap data + return std::string(reinterpret_cast<const char*>(&header), sizeof(bitmapfileheader)) + resource_data; +} +} diff --git a/pe_lib/resource_bitmap_reader.h b/pe_lib/resource_bitmap_reader.h new file mode 100644 index 0000000..ab7b3aa --- /dev/null +++ b/pe_lib/resource_bitmap_reader.h @@ -0,0 +1,29 @@ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_bitmap_reader +{ +public: + resource_bitmap_reader(const pe_resource_viewer& res); + + //Returns bitmap data by name and language (minimum checks of format correctness) + const std::string get_bitmap_by_name(uint32_t language, const std::wstring& name) const; + //Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_bitmap_by_name(const std::wstring& name, uint32_t index = 0) const; + //Returns bitmap data by ID and language (minimum checks of format correctness) + const std::string get_bitmap_by_id_lang(uint32_t language, uint32_t id) const; + //Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_bitmap_by_id(uint32_t id, uint32_t index = 0) const; + +private: + //Helper function of creating bitmap header + static const std::string create_bitmap(const std::string& resource_data); + + const pe_resource_viewer& res_; +}; +} diff --git a/pe_lib/resource_bitmap_writer.cpp b/pe_lib/resource_bitmap_writer.cpp new file mode 100644 index 0000000..743d8ed --- /dev/null +++ b/pe_lib/resource_bitmap_writer.cpp @@ -0,0 +1,54 @@ +#include "resource_bitmap_writer.h" +#include "pe_resource_manager.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_bitmap_writer::resource_bitmap_writer(pe_resource_manager& res) + :res_(res) +{} + +//Adds bitmap from bitmap file data. If bitmap already exists, replaces it +//timestamp will be used for directories that will be added +void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Check bitmap data a little + if(bitmap_file.length() < sizeof(bitmapfileheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + resource_directory_entry new_entry; + new_entry.set_id(id); + + //Add bitmap + res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} + +//Adds bitmap from bitmap file data. If bitmap already exists, replaces it +//timestamp will be used for directories that will be added +void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Check bitmap data a little + if(bitmap_file.length() < sizeof(bitmapfileheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + resource_directory_entry new_entry; + new_entry.set_name(name); + + //Add bitmap + res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Removes bitmap by name/ID and language +bool resource_bitmap_writer::remove_bitmap(const std::wstring& name, uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_bitmap, name, language); +} + +//Removes bitmap by name/ID and language +bool resource_bitmap_writer::remove_bitmap(uint32_t id, uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_bitmap, id, language); +} +} diff --git a/pe_lib/resource_bitmap_writer.h b/pe_lib/resource_bitmap_writer.h new file mode 100644 index 0000000..ed10faa --- /dev/null +++ b/pe_lib/resource_bitmap_writer.h @@ -0,0 +1,26 @@ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_bitmap_writer +{ +public: + resource_bitmap_writer(pe_resource_manager& res); + + //Adds bitmap from bitmap file data. If bitmap already exists, replaces it + //timestamp will be used for directories that will be added + void add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + + //Removes bitmap by name/ID and language + bool remove_bitmap(const std::wstring& name, uint32_t language); + bool remove_bitmap(uint32_t id, uint32_t language); + +private: + pe_resource_manager& res_; +}; +} diff --git a/pe_lib/resource_cursor_icon_reader.cpp b/pe_lib/resource_cursor_icon_reader.cpp new file mode 100644 index 0000000..7ad0b3e --- /dev/null +++ b/pe_lib/resource_cursor_icon_reader.cpp @@ -0,0 +1,500 @@ +#include <algorithm> +#include "resource_cursor_icon_reader.h" +#include "pe_structures.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_cursor_icon_reader::resource_cursor_icon_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Helper function of creating icon headers from ICON_GROUP resource data +//Returns icon count +uint16_t resource_cursor_icon_reader::format_icon_headers(std::string& ico_data, const std::string& resource_data) +{ + //Check resource data size + if(resource_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(resource_data.data()); + + //Check resource data size + if(resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Reserve memory to speed up a little + ico_data.reserve(sizeof(ico_header) + info->Count * sizeof(icondirentry)); + ico_data.append(reinterpret_cast<const char*>(info), sizeof(ico_header)); + + //Iterate over all listed icons + uint32_t offset = sizeof(ico_header) + sizeof(icondirentry) * info->Count; + for(uint16_t i = 0; i != info->Count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + + //Fill icon data + icondirentry direntry; + direntry.BitCount = group->BitCount; + direntry.ColorCount = group->ColorCount; + direntry.Height = group->Height; + direntry.Planes = group->Planes; + direntry.Reserved = group->Reserved; + direntry.SizeInBytes = group->SizeInBytes; + direntry.Width = group->Width; + direntry.ImageOffset = offset; + + //Add icon header to returned value + ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(icondirentry)); + + offset += group->SizeInBytes; + } + + //Return icon count + return info->Count; +} + +//Returns single icon data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_icon_by_id_lang(uint32_t language, uint32_t id) const +{ + //Get icon headers + std::string icon_data(lookup_icon_group_data_by_icon(id, language)); + //Append icon data + icon_data.append(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, id).get_data()); + return icon_data; +} + +//Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_icon_by_id(uint32_t id, uint32_t index) const +{ + pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_icon, id)); + if(languages.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + //Get icon headers + std::string icon_data(lookup_icon_group_data_by_icon(id, languages.at(index))); + //Append icon data + icon_data.append(res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, id, index).get_data()); + return icon_data; +} + +//Returns icon data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_name(const std::wstring& name, uint32_t index) const +{ + std::string ret; + + //Get resource by name and index + const std::string data = res_.get_resource_data_by_name(pe_resource_viewer::resource_icon_group, name, index).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data(); + } + + return ret; +} + +//Returns icon data by name and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_name(uint32_t language, const std::wstring& name) const +{ + std::string ret; + + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, name).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data(); + } + + return ret; +} + +//Returns icon data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string ret; + + //Get resource by language and id + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, id).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data(); + } + + return ret; +} + +//Returns icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_id(uint32_t id, uint32_t index) const +{ + std::string ret; + + //Get resource by id and index + const std::string data = res_.get_resource_data_by_id(pe_resource_viewer::resource_icon_group, id, index).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data(); + } + + return ret; +} + +//Checks for icon presence inside icon group, fills icon headers if found +bool resource_cursor_icon_reader::check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data) +{ + //Check resource data size + if(icon_group_resource_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_resource_data.data()); + + //Check resource data size + if(icon_group_resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + for(uint16_t i = 0; i != info->Count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + if(group->Number == icon_id) + { + //Reserve memory to speed up a little + ico_data.reserve(sizeof(ico_header) + sizeof(icondirentry)); + //Write single-icon icon header + ico_header new_header = *info; + new_header.Count = 1; + ico_data.append(reinterpret_cast<const char*>(&new_header), sizeof(ico_header)); + + //Fill icon data + icondirentry direntry; + direntry.BitCount = group->BitCount; + direntry.ColorCount = group->ColorCount; + direntry.Height = group->Height; + direntry.Planes = group->Planes; + direntry.Reserved = group->Reserved; + direntry.SizeInBytes = group->SizeInBytes; + direntry.Width = group->Width; + direntry.ImageOffset = sizeof(ico_header) + sizeof(icondirentry); + ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(direntry)); + + return true; + } + } + + return false; +} + +//Looks up icon group by icon id and returns full icon headers if found +const std::string resource_cursor_icon_reader::lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const +{ + std::string icon_header_data; + + { + //List all ID-resources + pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_icon_group)); + + for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_icon_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data)) + return icon_header_data; + } + } + + { + //List all named resources + pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_icon_group)); + for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_icon_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data)) + return icon_header_data; + } + } + + throw pe_exception("No icon group find for requested icon", pe_exception::no_icon_group_found); +} + +//Returns single cursor data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string raw_cursor_data(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, id).get_data()); + //Get cursor headers + std::string cursor_data(lookup_cursor_group_data_by_cursor(id, language, raw_cursor_data)); + //Append cursor data + cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */)); + return cursor_data; +} + +//Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_cursor_by_id(uint32_t id, uint32_t index) const +{ + pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor, id)); + if(languages.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + std::string raw_cursor_data(res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, id, index).get_data()); + //Get cursor headers + std::string cursor_data(lookup_cursor_group_data_by_cursor(id, languages.at(index), raw_cursor_data)); + //Append cursor data + cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */)); + return cursor_data; +} + +//Helper function of creating cursor headers +//Returns cursor count +uint16_t resource_cursor_icon_reader::format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index) const +{ + //Check resource data length + if(resource_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* info = reinterpret_cast<const cursor_header*>(resource_data.data()); + + //Check resource data length + if(resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group) * info->Count) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Reserve needed space to speed up a little + cur_data.reserve(sizeof(cursor_header) + info->Count * sizeof(cursordirentry)); + //Add cursor header + cur_data.append(reinterpret_cast<const char*>(info), sizeof(cursor_header)); + + //Iterate over all cursors listed in cursor group + uint32_t offset = sizeof(cursor_header) + sizeof(cursordirentry) * info->Count; + for(uint16_t i = 0; i != info->Count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + + //Fill cursor info + cursordirentry direntry; + direntry.ColorCount = 0; //OK + direntry.Width = static_cast<uint8_t>(group->Width); + direntry.Height = static_cast<uint8_t>(group->Height) / 2; + direntry.Reserved = 0; + + //Now read hotspot data from cursor data directory + const std::string cursor = index == 0xFFFFFFFF + ? res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data() + : res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data(); + if(cursor.length() < 2 * sizeof(uint16_t)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Here it is - two words in the very beginning of cursor data + direntry.HotspotX = *reinterpret_cast<const uint16_t*>(cursor.data()); + direntry.HotspotY = *reinterpret_cast<const uint16_t*>(cursor.data() + sizeof(uint16_t)); + + //Fill the rest data + direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t); + direntry.ImageOffset = offset; + + //Add cursor header + cur_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry)); + + offset += direntry.SizeInBytes; + } + + //Return cursor count + return info->Count; +} + +//Returns cursor data by name and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_name(uint32_t language, const std::wstring& name) const +{ + std::string ret; + + //Get resource by name and language + const std::string resource_data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, name).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_name(const std::wstring& name, uint32_t index) const +{ + std::string ret; + + //Get resource by name and index + const std::string resource_data = res_.get_resource_data_by_name(pe_resource_viewer::resource_cursor_group, name, index).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string ret; + + //Get resource by ID and language + const std::string resource_data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, id).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_id(uint32_t id, uint32_t index) const +{ + std::string ret; + + //Get resource by ID and index + const std::string resource_data = res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor_group, id, index).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Checks for cursor presence inside cursor group, fills cursor headers if found +bool resource_cursor_icon_reader::check_cursor_presence(const std::string& cursor_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data) +{ + //Check resource data length + if(cursor_group_resource_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_resource_data.data()); + + //Check resource data length + if(cursor_group_resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Iterate over all cursors listed in cursor group + for(uint16_t i = 0; i != info->Count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(cursor_group_resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + + if(group->Number == cursor_id) + { + //Reserve needed space to speed up a little + cur_header_data.reserve(sizeof(cursor_header) + sizeof(cursordirentry)); + //Write single-cursor cursor header + cursor_header new_header = *info; + new_header.Count = 1; + cur_header_data.append(reinterpret_cast<const char*>(&new_header), sizeof(cursor_header)); + + //Fill cursor info + cursordirentry direntry; + direntry.ColorCount = 0; //OK + direntry.Width = static_cast<uint8_t>(group->Width); + direntry.Height = static_cast<uint8_t>(group->Height) / 2; + direntry.Reserved = 0; + + if(raw_cursor_data.length() < 2 * sizeof(uint16_t)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Here it is - two words in the very beginning of cursor data + direntry.HotspotX = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data()); + direntry.HotspotY = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data() + sizeof(uint16_t)); + + //Fill the rest data + direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t); + direntry.ImageOffset = sizeof(cursor_header) + sizeof(cursordirentry); + + //Add cursor header + cur_header_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry)); + + return true; + } + } + + return false; +} + +//Looks up cursor group by cursor id and returns full cursor headers if found +const std::string resource_cursor_icon_reader::lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const +{ + std::string cursor_header_data; + + { + //List all ID-resources + pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_cursor_group)); + + for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_cursor_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data)) + return cursor_header_data; + } + } + + { + //List all named resources + pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_cursor_group)); + for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_cursor_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data)) + return cursor_header_data; + } + } + + throw pe_exception("No cursor group find for requested icon", pe_exception::no_cursor_group_found); +} +} diff --git a/pe_lib/resource_cursor_icon_reader.h b/pe_lib/resource_cursor_icon_reader.h new file mode 100644 index 0000000..2ceaa91 --- /dev/null +++ b/pe_lib/resource_cursor_icon_reader.h @@ -0,0 +1,63 @@ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_cursor_icon_reader +{ +public: + resource_cursor_icon_reader(const pe_resource_viewer& res); + + //Returns single icon data by ID and language (minimum checks of format correctness) + const std::string get_single_icon_by_id_lang(uint32_t language, uint32_t id) const; + //Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_single_icon_by_id(uint32_t id, uint32_t index = 0) const; + + //Returns icon data of group of icons by name and language (minimum checks of format correctness) + const std::string get_icon_by_name(uint32_t language, const std::wstring& icon_group_name) const; + //Returns icon data of group of icons by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_icon_by_name(const std::wstring& icon_group_name, uint32_t index = 0) const; + //Returns icon data of group of icons by ID and language (minimum checks of format correctness) + const std::string get_icon_by_id_lang(uint32_t language, uint32_t icon_group_id) const; + //Returns icon data of group of icons by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_icon_by_id(uint32_t icon_group_id, uint32_t index = 0) const; + + //Returns single cursor data by ID and language (minimum checks of format correctness) + const std::string get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const; + //Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_single_cursor_by_id(uint32_t id, uint32_t index = 0) const; + + //Returns cursor data by name and language (minimum checks of format correctness) + const std::string get_cursor_by_name(uint32_t language, const std::wstring& cursor_group_name) const; + //Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_cursor_by_name(const std::wstring& cursor_group_name, uint32_t index = 0) const; + //Returns cursor data by ID and language (minimum checks of format correctness) + const std::string get_cursor_by_id_lang(uint32_t language, uint32_t cursor_group_id) const; + //Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_cursor_by_id(uint32_t cursor_group_id, uint32_t index = 0) const; + +private: + const pe_resource_viewer& res_; + + //Helper function of creating icon headers from ICON_GROUP resource data + //Returns icon count + static uint16_t format_icon_headers(std::string& ico_data, const std::string& resource_data); + + //Helper function of creating cursor headers from CURSOR_GROUP resource data + //Returns cursor count + uint16_t format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index = 0xFFFFFFFF) const; + + //Looks up icon group by icon id and returns full icon headers if found + const std::string lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const; + //Checks for icon presence inside icon group, fills icon headers if found + static bool check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data); + + //Looks up cursor group by cursor id and returns full cursor headers if found + const std::string lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const; + //Checks for cursor presence inside cursor group, fills cursor headers if found + static bool check_cursor_presence(const std::string& icon_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data); +}; +} diff --git a/pe_lib/resource_cursor_icon_writer.cpp b/pe_lib/resource_cursor_icon_writer.cpp new file mode 100644 index 0000000..5c09860 --- /dev/null +++ b/pe_lib/resource_cursor_icon_writer.cpp @@ -0,0 +1,426 @@ +#include <algorithm> +#include <string.h> +#include "resource_cursor_icon_writer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_cursor_icon_writer::resource_cursor_icon_writer(pe_resource_manager& res) + :res_(res) +{} + +//Add icon helper +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + //Check icon for correctness + if(icon_file.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + const ico_header* icon_header = reinterpret_cast<const ico_header*>(&icon_file[0]); + + unsigned long size_of_headers = sizeof(ico_header) + icon_header->Count * sizeof(icondirentry); + if(icon_file.length() < size_of_headers || icon_header->Count == 0) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Enumerate all icons in file + for(uint16_t i = 0; i != icon_header->Count; ++i) + { + //Check icon entries + const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); + if(icon_entry->SizeInBytes == 0 + || icon_entry->ImageOffset < size_of_headers + || !pe_utils::is_sum_safe(icon_entry->ImageOffset, icon_entry->SizeInBytes) + || icon_entry->ImageOffset + icon_entry->SizeInBytes > icon_file.length()) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + } + + std::string icon_group_data; + ico_header* info = 0; + + if(group_icon_info) + { + //If icon group already exists + { + icon_group_data = group_icon_info->get_data(); + codepage = group_icon_info->get_codepage(); //Don't change codepage of icon group entry + } + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + info = reinterpret_cast<ico_header*>(&icon_group_data[0]); + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + icon_group_data.resize(sizeof(ico_header) + (info->Count + icon_header->Count) * sizeof(icon_group)); + info = reinterpret_cast<ico_header*>(&icon_group_data[0]); //In case if memory was reallocated + } + else //Entry not found - icon group doesn't exist + { + icon_group_data.resize(sizeof(ico_header) + icon_header->Count * sizeof(icon_group)); + memcpy(&icon_group_data[0], icon_header, sizeof(ico_header)); + } + + //Search for available icon IDs + std::vector<uint16_t> icon_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_icon, mode, icon_header->Count)); + + //Enumerate all icons in file + for(uint16_t i = 0; i != icon_header->Count; ++i) + { + const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); + icon_group group = {0}; + + //Fill icon resource header + group.BitCount = icon_entry->BitCount; + group.ColorCount = icon_entry->ColorCount; + group.Height = icon_entry->Height; + group.Planes = icon_entry->Planes; + group.Reserved = icon_entry->Reserved; + group.SizeInBytes = icon_entry->SizeInBytes; + group.Width = icon_entry->Width; + group.Number = icon_id_list.at(i); + + memcpy(&icon_group_data[sizeof(ico_header) + ((info ? info->Count : 0) + i) * sizeof(icon_group)], &group, sizeof(group)); + + //Add icon to resources + resource_directory_entry new_entry; + new_entry.set_id(group.Number); + res_.add_resource(icon_file.substr(icon_entry->ImageOffset, icon_entry->SizeInBytes), pe_resource_viewer::resource_icon, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp); + } + + if(info) + info->Count += icon_header->Count; //Increase icon count, if we're adding icon to existing group + + { + //Add or replace icon group data entry + res_.add_resource(icon_group_data, pe_resource_viewer::resource_icon_group, new_icon_group_entry, finder, language, codepage, timestamp); + } +} + +//Returns free icon or cursor ID list depending on icon_place_mode +const std::vector<uint16_t> resource_cursor_icon_writer::get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_type type, icon_place_mode mode, uint32_t count) +{ + //Search for available icon/cursor IDs + std::vector<uint16_t> icon_cursor_id_list; + + try + { + //If any icon exists + //List icon IDs + std::vector<uint32_t> id_list(res_.list_resource_ids(type)); + std::sort(id_list.begin(), id_list.end()); + + //If we are placing icon on free spaces + //I.e., icon IDs 1, 3, 4, 7, 8 already exist + //We'll place five icons on IDs 2, 5, 6, 9, 10 + if(mode != icon_place_after_max_icon_id) + { + if(!id_list.empty()) + { + //Determine and list free icon IDs + for(std::vector<uint32_t>::const_iterator it = id_list.begin(); it != id_list.end(); ++it) + { + if(it == id_list.begin()) + { + if(*it > 1) + { + for(uint16_t i = 1; i != *it; ++i) + { + icon_cursor_id_list.push_back(i); + if(icon_cursor_id_list.size() == count) + break; + } + } + } + else if(*(it - 1) - *it > 1) + { + for(uint16_t i = static_cast<uint16_t>(*(it - 1) + 1); i != static_cast<uint16_t>(*it); ++i) + { + icon_cursor_id_list.push_back(i); + if(icon_cursor_id_list.size() == count) + break; + } + } + + if(icon_cursor_id_list.size() == count) + break; + } + } + } + + uint32_t max_id = id_list.empty() ? 0 : *std::max_element(id_list.begin(), id_list.end()); + for(uint32_t i = static_cast<uint32_t>(icon_cursor_id_list.size()); i != count; ++i) + icon_cursor_id_list.push_back(static_cast<uint16_t>(++max_id)); + } + catch(const pe_exception&) //Entry not found + { + for(uint16_t i = 1; i != count + 1; ++i) + icon_cursor_id_list.push_back(i); + } + + return icon_cursor_id_list; +} + +//Add cursor helper +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + //Check cursor for correctness + if(cursor_file.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* cur_header = reinterpret_cast<const cursor_header*>(&cursor_file[0]); + + unsigned long size_of_headers = sizeof(cursor_header) + cur_header->Count * sizeof(cursordirentry); + if(cursor_file.length() < size_of_headers || cur_header->Count == 0) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Enumerate all cursors in file + for(uint16_t i = 0; i != cur_header->Count; ++i) + { + //Check cursor entries + const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); + if(cursor_entry->SizeInBytes == 0 + || cursor_entry->ImageOffset < size_of_headers + || !pe_utils::is_sum_safe(cursor_entry->ImageOffset, cursor_entry->SizeInBytes) + || cursor_entry->ImageOffset + cursor_entry->SizeInBytes > cursor_file.length()) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + } + + std::string cursor_group_data; + cursor_header* info = 0; + + if(group_cursor_info) + { + //If cursor group already exists + { + cursor_group_data = group_cursor_info->get_data(); + codepage = group_cursor_info->get_codepage(); //Don't change codepage of cursor group entry + } + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Get cursor header + info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header) + info->Count * sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + cursor_group_data.resize(sizeof(cursor_header) + (info->Count + cur_header->Count) * sizeof(cursor_group)); + info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); //In case if memory was reallocated + } + else //Entry not found - cursor group doesn't exist + { + cursor_group_data.resize(sizeof(cursor_header) + cur_header->Count * sizeof(cursor_group)); + memcpy(&cursor_group_data[0], cur_header, sizeof(cursor_header)); + } + + //Search for available cursor IDs + std::vector<uint16_t> cursor_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_cursor, mode, cur_header->Count)); + + //Enumerate all cursors in file + for(uint16_t i = 0; i != cur_header->Count; ++i) + { + const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); + cursor_group group = {0}; + + //Fill cursor resource header + group.Height = cursor_entry->Height * 2; + group.SizeInBytes = cursor_entry->SizeInBytes + 2 * sizeof(uint16_t) /* hotspot coordinates */; + group.Width = cursor_entry->Width; + group.Number = cursor_id_list.at(i); + + memcpy(&cursor_group_data[sizeof(cursor_header) + ((info ? info->Count : 0) + i) * sizeof(cursor_group)], &group, sizeof(group)); + + //Add cursor to resources + resource_directory_entry new_entry; + new_entry.set_id(group.Number); + + //Fill resource data (two WORDs for hotspot of cursor, and cursor bitmap data) + std::string cur_data; + cur_data.resize(sizeof(uint16_t) * 2); + memcpy(&cur_data[0], &cursor_entry->HotspotX, sizeof(uint16_t)); + memcpy(&cur_data[sizeof(uint16_t)], &cursor_entry->HotspotY, sizeof(uint16_t)); + cur_data.append(cursor_file.substr(cursor_entry->ImageOffset, cursor_entry->SizeInBytes)); + + res_.add_resource(cur_data, pe_resource_viewer::resource_cursor, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp); + } + + if(info) + info->Count += cur_header->Count; //Increase cursor count, if we're adding cursor to existing group + + { + //Add or replace cursor group data entry + res_.add_resource(cursor_group_data, pe_resource_viewer::resource_cursor_group, new_cursor_group_entry, finder, language, codepage, timestamp); + } +} + +//Adds icon(s) from icon file data +//timestamp will be used for directories that will be added +//If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) +//(Codepage of icon group and icons will not be changed in this case) +//icon_place_mode determines, how new icon(s) will be placed +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const std::wstring& icon_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_icon_group_entry; + new_icon_group_entry.set_name(icon_group_name); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_name), language, mode, codepage, timestamp); +} + +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, uint32_t icon_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_icon_group_entry; + new_icon_group_entry.set_id(icon_group_id); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_id), language, mode, codepage, timestamp); +} + +//Adds cursor(s) from cursor file data +//timestamp will be used for directories that will be added +//If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) +//(Codepage of cursor group and cursors will not be changed in this case) +//icon_place_mode determines, how new cursor(s) will be placed +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_cursor_group_entry; + new_cursor_group_entry.set_name(cursor_group_name); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_name), language, mode, codepage, timestamp); +} + +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_cursor_group_entry; + new_cursor_group_entry.set_id(cursor_group_id); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_id), language, mode, codepage, timestamp); +} + +//Remove icon group helper +void resource_cursor_icon_writer::remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language) +{ + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_data.data()); + + uint16_t icon_count = info->Count; + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header) + icon_count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Remove icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + res_.remove_resource(pe_resource_viewer::resource_icon, group->Number, language); + } +} + +//Remove cursor group helper +void resource_cursor_icon_writer::remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language) +{ + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Get icon header + const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_data.data()); + + uint16_t cursor_count = info->Count; + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header) + cursor_count * sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Remove icon data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(cursor_group_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + res_.remove_resource(pe_resource_viewer::resource_cursor, group->Number, language); + } +} + +//Removes cursor group and all its cursors by name/ID and language +bool resource_cursor_icon_writer::remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name).get_data(); + remove_cursors_from_cursor_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_name, language); +} + +//Removes cursor group and all its cursors by name/ID and language +bool resource_cursor_icon_writer::remove_cursor_group(uint32_t cursor_group_id, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id).get_data(); + remove_cursors_from_cursor_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_id, language); +} + +//Removes icon group and all its icons by name/ID and language +bool resource_cursor_icon_writer::remove_icon_group(const std::wstring& icon_group_name, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name).get_data(); + remove_icons_from_icon_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_name, language); +} + +//Removes icon group and all its icons by name/ID and language +bool resource_cursor_icon_writer::remove_icon_group(uint32_t icon_group_id, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id).get_data(); + remove_icons_from_icon_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_id, language); +} +} diff --git a/pe_lib/resource_cursor_icon_writer.h b/pe_lib/resource_cursor_icon_writer.h new file mode 100644 index 0000000..b3d89cc --- /dev/null +++ b/pe_lib/resource_cursor_icon_writer.h @@ -0,0 +1,73 @@ +#pragma once +#include <string> +#include <vector> +#include "stdint_defs.h" +#include "pe_resource_manager.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_cursor_icon_writer +{ +public: + //Determines, how new icon(s) or cursor(s) will be placed + enum icon_place_mode + { + icon_place_after_max_icon_id, //Icon(s) will be placed after all existing + icon_place_free_ids //New icon(s) will take all free IDs between existing icons + }; + +public: + resource_cursor_icon_writer(pe_resource_manager& res); + + //Removes icon group and all its icons by name/ID and language + bool remove_icon_group(const std::wstring& icon_group_name, uint32_t language); + bool remove_icon_group(uint32_t icon_group_id, uint32_t language); + + //Adds icon(s) from icon file data + //timestamp will be used for directories that will be added + //If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) + //(Codepage of icon group and icons will not be changed in this case) + //icon_place_mode determines, how new icon(s) will be placed + void add_icon(const std::string& icon_file, + const std::wstring& icon_group_name, + uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, + uint32_t codepage = 0, uint32_t timestamp = 0); + + void add_icon(const std::string& icon_file, + uint32_t icon_group_id, + uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, + uint32_t codepage = 0, uint32_t timestamp = 0); + + //Removes cursor group and all its cursors by name/ID and language + bool remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language); + bool remove_cursor_group(uint32_t cursor_group_id, uint32_t language); + + //Adds cursor(s) from cursor file data + //timestamp will be used for directories that will be added + //If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) + //(Codepage of cursor group and cursors will not be changed in this case) + //icon_place_mode determines, how new cursor(s) will be placed + void add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); + +private: + pe_resource_manager& res_; + + //Add icon helper + void add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); + + //Remove icon group helper + void remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language); + + //Add cursor helper + void add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); + + //Remove cursor group helper + void remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language); + + //Returns free icon or cursor ID list depending on icon_place_mode + const std::vector<uint16_t> get_icon_or_cursor_free_id_list(pe_resource_manager::resource_type type, icon_place_mode mode, uint32_t count); +}; +} diff --git a/pe_lib/resource_data_info.cpp b/pe_lib/resource_data_info.cpp new file mode 100644 index 0000000..3dafcf4 --- /dev/null +++ b/pe_lib/resource_data_info.cpp @@ -0,0 +1,27 @@ +#include "resource_data_info.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +//Default constructor +resource_data_info::resource_data_info(const std::string& data, uint32_t codepage) + :data_(data), codepage_(codepage) +{} + +//Constructor from data +resource_data_info::resource_data_info(const resource_data_entry& data) + :data_(data.get_data()), codepage_(data.get_codepage()) +{} + +//Returns resource data +const std::string& resource_data_info::get_data() const +{ + return data_; +} + +//Returns resource codepage +uint32_t resource_data_info::get_codepage() const +{ + return codepage_; +} +} diff --git a/pe_lib/resource_data_info.h b/pe_lib/resource_data_info.h new file mode 100644 index 0000000..84768c8 --- /dev/null +++ b/pe_lib/resource_data_info.h @@ -0,0 +1,27 @@ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class resource_data_entry; + +//Class representing resource data +class resource_data_info +{ +public: + //Constructor from data + resource_data_info(const std::string& data, uint32_t codepage); + //Constructor from data + explicit resource_data_info(const resource_data_entry& data); + + //Returns resource data + const std::string& get_data() const; + //Returns resource codepage + uint32_t get_codepage() const; + +private: + std::string data_; + uint32_t codepage_; +}; +} diff --git a/pe_lib/resource_internal.h b/pe_lib/resource_internal.h new file mode 100644 index 0000000..7cc3386 --- /dev/null +++ b/pe_lib/resource_internal.h @@ -0,0 +1,13 @@ +#pragma once + +#define U16TEXT(t) reinterpret_cast<const unicode16_t*>( t ) + +#define StringFileInfo U16TEXT("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define SizeofStringFileInfo sizeof("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define VarFileInfo U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define Translation U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0") + +#define VarFileInfoAligned U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") +#define TranslationAligned U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") +#define SizeofVarFileInfoAligned sizeof("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") +#define SizeofTranslationAligned sizeof("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") diff --git a/pe_lib/resource_message_list_reader.cpp b/pe_lib/resource_message_list_reader.cpp new file mode 100644 index 0000000..ee4ca36 --- /dev/null +++ b/pe_lib/resource_message_list_reader.cpp @@ -0,0 +1,110 @@ +#include "resource_message_list_reader.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_message_list_reader::resource_message_list_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Helper function of parsing message list table +const resource_message_list resource_message_list_reader::parse_message_list(const std::string& resource_data) +{ + resource_message_list ret; + + //Check resource data length + if(resource_data.length() < sizeof(message_resource_data)) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + const message_resource_data* message_data = reinterpret_cast<const message_resource_data*>(resource_data.data()); + + //Check resource data length more carefully and some possible overflows + if(message_data->NumberOfBlocks >= pe_utils::max_dword / sizeof(message_resource_block) + || !pe_utils::is_sum_safe(message_data->NumberOfBlocks * sizeof(message_resource_block), sizeof(message_resource_data)) + || resource_data.length() < message_data->NumberOfBlocks * sizeof(message_resource_block) + sizeof(message_resource_data)) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Iterate over all message resource blocks + for(unsigned long i = 0; i != message_data->NumberOfBlocks; ++i) + { + //Get block + const message_resource_block* block = + reinterpret_cast<const message_resource_block*>(resource_data.data() + sizeof(message_resource_data) - sizeof(message_resource_block) + sizeof(message_resource_block) * i); + + //Check resource data length and IDs + if(resource_data.length() < block->OffsetToEntries || block->LowId > block->HighId) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + unsigned long current_pos = 0; + static const unsigned long size_of_entry_headers = 4; + //List all message resource entries in block + for(uint32_t curr_id = block->LowId; curr_id <= block->HighId; curr_id++) + { + //Check resource data length and some possible overflows + if(!pe_utils::is_sum_safe(block->OffsetToEntries, current_pos) + || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, size_of_entry_headers) + || resource_data.length() < block->OffsetToEntries + current_pos + size_of_entry_headers) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Get entry + const message_resource_entry* entry = reinterpret_cast<const message_resource_entry*>(resource_data.data() + block->OffsetToEntries + current_pos); + + //Check resource data length and entry length and some possible overflows + if(entry->Length < size_of_entry_headers + || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, entry->Length) + || resource_data.length() < block->OffsetToEntries + current_pos + entry->Length + || entry->Length < size_of_entry_headers) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + if(entry->Flags & message_resource_unicode) + { + //If string is UNICODE + //Check its length + if(entry->Length % 2) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Add ID and string to message table +#ifdef PE_BLISS_WINDOWS + ret.insert(std::make_pair(curr_id, message_table_item( + std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), + (entry->Length - size_of_entry_headers) / 2) + ))); +#else + ret.insert(std::make_pair(curr_id, message_table_item( + pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), + (entry->Length - size_of_entry_headers) / 2)) + ))); +#endif + } + else + { + //If string is ANSI + //Add ID and string to message table + ret.insert(std::make_pair(curr_id, message_table_item( + std::string(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers, + entry->Length - size_of_entry_headers) + ))); + } + + //Go to next entry + current_pos += entry->Length; + } + } + + return ret; +} + +//Returns message table data by ID and index in language directory (instead of language) +const resource_message_list resource_message_list_reader::get_message_table_by_id(uint32_t id, uint32_t index) const +{ + return parse_message_list(res_.get_resource_data_by_id(pe_resource_viewer::resource_message_table, id, index).get_data()); +} + +//Returns message table data by ID and language +const resource_message_list resource_message_list_reader::get_message_table_by_id_lang(uint32_t language, uint32_t id) const +{ + return parse_message_list(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_message_table, id).get_data()); +} +} diff --git a/pe_lib/resource_message_list_reader.h b/pe_lib/resource_message_list_reader.h new file mode 100644 index 0000000..5fa0379 --- /dev/null +++ b/pe_lib/resource_message_list_reader.h @@ -0,0 +1,28 @@ +#pragma once +#include "message_table.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +//ID; message_table_item +typedef std::map<uint32_t, message_table_item> resource_message_list; + +class resource_message_list_reader +{ +public: + resource_message_list_reader(const pe_resource_viewer& res); + + //Returns message table data by ID and language + const resource_message_list get_message_table_by_id_lang(uint32_t language, uint32_t id) const; + //Returns message table data by ID and index in language directory (instead of language) + const resource_message_list get_message_table_by_id(uint32_t id, uint32_t index = 0) const; + + //Helper function of parsing message list table + //resource_data - raw message table resource data + static const resource_message_list parse_message_list(const std::string& resource_data); + +private: + const pe_resource_viewer& res_; +}; +} diff --git a/pe_lib/resource_string_table_reader.cpp b/pe_lib/resource_string_table_reader.cpp new file mode 100644 index 0000000..1bfe9be --- /dev/null +++ b/pe_lib/resource_string_table_reader.cpp @@ -0,0 +1,88 @@ +#include "resource_string_table_reader.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +resource_string_table_reader::resource_string_table_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns string table data by ID and index in language directory (instead of language) +const resource_string_list resource_string_table_reader::get_string_table_by_id(uint32_t id, uint32_t index) const +{ + return parse_string_list(id, res_.get_resource_data_by_id(pe_resource_viewer::resource_string, id, index).get_data()); +} + +//Returns string table data by ID and language +const resource_string_list resource_string_table_reader::get_string_table_by_id_lang(uint32_t language, uint32_t id) const +{ + return parse_string_list(id, res_.get_resource_data_by_id(language, pe_resource_viewer::resource_string, id).get_data()); +} + +//Helper function of parsing string list table +const resource_string_list resource_string_table_reader::parse_string_list(uint32_t id, const std::string& resource_data) +{ + resource_string_list ret; + + //16 is maximum count of strings in a string table + static const unsigned long max_string_list_entries = 16; + unsigned long passed_bytes = 0; + for(unsigned long i = 0; i != max_string_list_entries; ++i) + { + //Check resource data length + if(resource_data.length() < sizeof(uint16_t) + passed_bytes) + throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); + + //Get string length - the first WORD + uint16_t string_length = *reinterpret_cast<const uint16_t*>(resource_data.data() + passed_bytes); + passed_bytes += sizeof(uint16_t); //WORD containing string length + + //Check resource data length again + if(resource_data.length() < string_length + passed_bytes) + throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); + + if(string_length) + { + //Create and save string (UNICODE) +#ifdef PE_BLISS_WINDOWS + ret.insert( + std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way + std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + passed_bytes), string_length))); +#else + ret.insert( + std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way + pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + passed_bytes), string_length)))); +#endif + } + + //Go to next string + passed_bytes += string_length * 2; + } + + return ret; +} + +//Returns string from string table by ID and language +const std::wstring resource_string_table_reader::get_string_by_id_lang(uint32_t language, uint16_t id) const +{ + //List strings by string table id and language + const resource_string_list strings(get_string_table_by_id_lang(language, (id >> 4) + 1)); + resource_string_list::const_iterator it = strings.find(id); //Find string by id + if(it == strings.end()) + throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); + + return (*it).second; +} + +//Returns string from string table by ID and index in language directory (instead of language) +const std::wstring resource_string_table_reader::get_string_by_id(uint16_t id, uint32_t index) const +{ + //List strings by string table id and index + const resource_string_list strings(get_string_table_by_id((id >> 4) + 1, index)); + resource_string_list::const_iterator it = strings.find(id); //Find string by id + if(it == strings.end()) + throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); + + return (*it).second; +} +} diff --git a/pe_lib/resource_string_table_reader.h b/pe_lib/resource_string_table_reader.h new file mode 100644 index 0000000..ec98a70 --- /dev/null +++ b/pe_lib/resource_string_table_reader.h @@ -0,0 +1,36 @@ +#pragma once +#include <string> +#include <map> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +//ID; string +typedef std::map<uint16_t, std::wstring> resource_string_list; + +class resource_string_table_reader +{ +public: + resource_string_table_reader(const pe_resource_viewer& res); + +public: + //Returns string table data by ID and language + const resource_string_list get_string_table_by_id_lang(uint32_t language, uint32_t id) const; + //Returns string table data by ID and index in language directory (instead of language) + const resource_string_list get_string_table_by_id(uint32_t id, uint32_t index = 0) const; + //Returns string from string table by ID and language + const std::wstring get_string_by_id_lang(uint32_t language, uint16_t id) const; + //Returns string from string table by ID and index in language directory (instead of language) + const std::wstring get_string_by_id(uint16_t id, uint32_t index = 0) const; + +private: + const pe_resource_viewer& res_; + + //Helper function of parsing string list table + //Id of resource is needed to calculate string IDs correctly + //resource_data is raw string table resource data + static const resource_string_list parse_string_list(uint32_t id, const std::string& resource_data); +}; +} diff --git a/pe_lib/resource_version_info_reader.cpp b/pe_lib/resource_version_info_reader.cpp new file mode 100644 index 0000000..324d16b --- /dev/null +++ b/pe_lib/resource_version_info_reader.cpp @@ -0,0 +1,290 @@ +#include "resource_version_info_reader.h" +#include "utils.h" +#include "pe_exception.h" +#include "resource_internal.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Root version info block key value +const u16string resource_version_info_reader::version_info_key(U16TEXT("V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0\0")); + +resource_version_info_reader::resource_version_info_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns aligned version block value position +uint32_t resource_version_info_reader::get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key) +{ + uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); + uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ + + base_pos + + (string_length + 1 /* nullbyte */) * 2), + sizeof(uint32_t)); + + //Check possible overflows + if(ret < base_pos || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) + throw_incorrect_version_info(); + + return ret; +} + +//Returns aligned version block first child position +uint32_t resource_version_info_reader::get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key) +{ + uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); + uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ + + base_pos + + (string_length + 1 /* nullbyte */) * 2), + sizeof(uint32_t)) + + pe_utils::align_up(value_length, sizeof(uint32_t)); + + //Check possible overflows + if(ret < base_pos || ret < value_length || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) + throw_incorrect_version_info(); + + return ret; +} + +//Throws an exception (id = resource_incorrect_version_info) +void resource_version_info_reader::throw_incorrect_version_info() +{ + throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); +} + +//Returns full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const +{ + //Fixed file version info + file_version_info ret; + + //Check resource data length + if(resource_data.length() < sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Root version info block + const version_info_block* root_block = reinterpret_cast<const version_info_block*>(resource_data.data()); + + //Check root block key for null-termination and its name + if(!pe_utils::is_null_terminated(root_block->Key, resource_data.length() - sizeof(uint16_t) * 3 /* headers before Key data */) + || version_info_key != reinterpret_cast<const unicode16_t*>(root_block->Key)) + throw_incorrect_version_info(); + + //If file has fixed version info + if(root_block->ValueLength) + { + //Get root block value position + uint32_t value_pos = get_version_block_value_pos(0, reinterpret_cast<const unicode16_t*>(root_block->Key)); + //Check value length + if(resource_data.length() < value_pos + sizeof(vs_fixedfileinfo)) + throw_incorrect_version_info(); + + //Get VS_FIXEDFILEINFO structure pointer + const vs_fixedfileinfo* file_info = reinterpret_cast<const vs_fixedfileinfo*>(resource_data.data() + value_pos); + //Check its signature and some other fields + if(file_info->dwSignature != vs_ffi_signature || file_info->dwStrucVersion != vs_ffi_strucversion) //Don't check if file_info->dwFileFlagsMask == VS_FFI_FILEFLAGSMASK + throw_incorrect_version_info(); + + //Save fixed version info + ret = file_version_info(*file_info); + } + + //Iterate over child elements of VS_VERSIONINFO (StringFileInfo or VarFileInfo) + for(uint32_t child_pos = get_version_block_first_child_pos(0, root_block->ValueLength, reinterpret_cast<const unicode16_t*>(root_block->Key)); + child_pos < root_block->Length;) + { + //Check block position + if(!pe_utils::is_sum_safe(child_pos, sizeof(version_info_block)) + || resource_data.length() < child_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer + const version_info_block* block = reinterpret_cast<const version_info_block*>(resource_data.data() + child_pos); + + //Check its length + if(block->Length == 0) + throw_incorrect_version_info(); + + //Check block key for null-termination + if(!pe_utils::is_null_terminated(block->Key, resource_data.length() - child_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + u16string info_type(reinterpret_cast<const unicode16_t*>(block->Key)); + //If we encountered StringFileInfo... + if(info_type == StringFileInfo) + { + //Enumerate all string tables + for(uint32_t string_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); + string_table_pos - child_pos < block->Length;) + { + //Check string table block position + if(resource_data.length() < string_table_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for string table + const version_info_block* string_table = reinterpret_cast<const version_info_block*>(resource_data.data() + string_table_pos); + + //Check its length + if(string_table->Length == 0) + throw_incorrect_version_info(); + + //Check string table key for null-termination + if(!pe_utils::is_null_terminated(string_table->Key, resource_data.length() - string_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + string_values_map new_values; + + //Enumerate all strings in the string table + for(uint32_t string_pos = get_version_block_first_child_pos(string_table_pos, string_table->ValueLength, reinterpret_cast<const unicode16_t*>(string_table->Key)); + string_pos - string_table_pos < string_table->Length;) + { + //Check string block position + if(resource_data.length() < string_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for string block + const version_info_block* string_block = reinterpret_cast<const version_info_block*>(resource_data.data() + string_pos); + + //Check its length + if(string_block->Length == 0) + throw_incorrect_version_info(); + + //Check string block key for null-termination + if(!pe_utils::is_null_terminated(string_block->Key, resource_data.length() - string_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + u16string data; + //If string block has value + if(string_block->ValueLength != 0) + { + //Get value position + uint32_t value_pos = get_version_block_value_pos(string_pos, reinterpret_cast<const unicode16_t*>(string_block->Key)); + //Check it + if(resource_data.length() < value_pos + string_block->ValueLength) + throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); + + //Get UNICODE string value + data = u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + value_pos), string_block->ValueLength); + pe_utils::strip_nullbytes(data); + } + + //Save name-value pair +#ifdef PE_BLISS_WINDOWS + new_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_block->Key), data)); +#else + new_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_block->Key)), + pe_utils::from_ucs2(data))); +#endif + + //Navigate to next string block + string_pos += pe_utils::align_up(string_block->Length, sizeof(uint32_t)); + } + +#ifdef PE_BLISS_WINDOWS + string_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_table->Key), new_values)); +#else + string_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_table->Key)), new_values)); +#endif + + //Navigate to next string table block + string_table_pos += pe_utils::align_up(string_table->Length, sizeof(uint32_t)); + } + } + else if(info_type == VarFileInfo) //If we encountered VarFileInfo + { + for(uint32_t var_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); + var_table_pos - child_pos < block->Length;) + { + //Check var block position + if(resource_data.length() < var_table_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for var block + const version_info_block* var_table = reinterpret_cast<const version_info_block*>(resource_data.data() + var_table_pos); + + //Check its length + if(var_table->Length == 0) + throw_incorrect_version_info(); + + //Check its key for null-termination + if(!pe_utils::is_null_terminated(var_table->Key, resource_data.length() - var_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + //If block is "Translation" (actually, there's no other types possible in VarFileInfo) and it has value + if(u16string(reinterpret_cast<const unicode16_t*>(var_table->Key)) == Translation && var_table->ValueLength) + { + //Get its value position + uint32_t value_pos = get_version_block_value_pos(var_table_pos, reinterpret_cast<const unicode16_t*>(var_table->Key)); + //Cherck value length + if(resource_data.length() < value_pos + var_table->ValueLength) + throw_incorrect_version_info(); + + //Get list of translations: pairs of LANGUAGE_ID - CODEPAGE_ID + for(unsigned long i = 0; i < var_table->ValueLength; i += sizeof(uint16_t) * 2) + { + //Pair of WORDs + uint16_t lang_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + i); + uint16_t codepage_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + sizeof(uint16_t) + i); + //Save translation + translations.insert(std::make_pair(lang_id, codepage_id)); + } + } + + //Navigate to next var block + var_table_pos += pe_utils::align_up(var_table->Length, sizeof(uint32_t)); + } + } + else + { + throw_incorrect_version_info(); + } + + //Navigate to next element in root block + child_pos += pe_utils::align_up(block->Length, sizeof(uint32_t)); + } + + return ret; +} + +//Returns full version information: +//file_version info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const +{ + const std::string& resource_data = res_.get_root_directory() //Type directory + .entry_by_id(pe_resource_viewer::resource_version) + .get_resource_directory() //Name/ID directory + .entry_by_id(1) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry() //Data directory + .get_data(); + + return get_version_info(string_values, translations, resource_data); +} + +//Returns full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index) const +{ + const resource_directory::entry_list& entries = res_.get_root_directory() //Type directory + .entry_by_id(pe_resource_viewer::resource_version) + .get_resource_directory() //Name/ID directory + .entry_by_id(1) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return get_version_info(string_values, translations, entries.at(index).get_data_entry().get_data()); //Data directory +} +} diff --git a/pe_lib/resource_version_info_reader.h b/pe_lib/resource_version_info_reader.h new file mode 100644 index 0000000..85817ea --- /dev/null +++ b/pe_lib/resource_version_info_reader.h @@ -0,0 +1,46 @@ +#pragma once +#include <map> +#include "file_version_info.h" +#include "pe_structures.h" +#include "version_info_types.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_version_info_reader +{ +public: //VERSION INFO + resource_version_info_reader(const pe_resource_viewer& res); + + //Returns full version information: + //file_version_info: versions and file info + //lang_lang_string_values_map: map of version info strings with encodings with encodings + //translation_values_map: map of translations + const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index = 0) const; + const file_version_info get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const; + +public: + //L"VS_VERSION_INFO" key of root version info block + static const u16string version_info_key; + +private: + const pe_resource_viewer& res_; + + //VERSION INFO helpers + //Returns aligned version block value position + static uint32_t get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key); + + //Returns aligned version block first child position + static uint32_t get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key); + + //Returns full version information: + //file_version_info: versions and file info + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const; + + //Throws an exception (id = resource_incorrect_version_info) + static void throw_incorrect_version_info(); +}; +} diff --git a/pe_lib/resource_version_info_writer.cpp b/pe_lib/resource_version_info_writer.cpp new file mode 100644 index 0000000..5af8cd1 --- /dev/null +++ b/pe_lib/resource_version_info_writer.cpp @@ -0,0 +1,262 @@ +#include <string.h> +#include "resource_version_info_writer.h" +#include "pe_structures.h" +#include "resource_internal.h" +#include "utils.h" +#include "pe_resource_manager.h" +#include "resource_version_info_reader.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_version_info_writer::resource_version_info_writer(pe_resource_manager& res) + :res_(res) +{} + +//Sets/replaces full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +void resource_version_info_writer::set_version_info(const file_version_info& file_info, + const lang_string_values_map& string_values, + const translation_values_map& translations, + uint32_t language, + uint32_t codepage, + uint32_t timestamp) +{ + std::string version_data; + + //Calculate total size of version resource data + uint32_t total_version_info_length = + static_cast<uint32_t>(sizeof(version_info_block) - sizeof(uint16_t) + sizeof(uint16_t) /* pading */ + + (resource_version_info_reader::version_info_key.length() + 1) * 2 + + sizeof(vs_fixedfileinfo)); + + //If we have any strings values + if(!string_values.empty()) + { + total_version_info_length += sizeof(version_info_block) - sizeof(uint16_t); //StringFileInfo block + total_version_info_length += SizeofStringFileInfo; //Name of block (key) + + //Add required size for version strings + for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) + { + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*table_it).first.length() + 1) * 2), sizeof(uint32_t)); //Name of child block and block size (key of string table block) + + const string_values_map& values = (*table_it).second; + for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) + { + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*it).first.length() + 1) * 2), sizeof(uint32_t)); + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(((*it).second.length() + 1) * 2), sizeof(uint32_t)); + } + } + } + + //If we have translations + if(!translations.empty()) + { + total_version_info_length += (sizeof(version_info_block) - sizeof(uint16_t)) * 2; //VarFileInfo and Translation blocks + total_version_info_length += SizeofVarFileInfoAligned; //DWORD-aligned VarFileInfo block name + total_version_info_length += SizeofTranslationAligned; //DWORD-aligned Translation block name + total_version_info_length += static_cast<uint32_t>(translations.size() * sizeof(uint16_t) * 2); + } + + //Resize version data buffer + version_data.resize(total_version_info_length); + + //Create root version block + version_info_block root_block = {0}; + root_block.ValueLength = sizeof(vs_fixedfileinfo); + root_block.Length = static_cast<uint16_t>(total_version_info_length); + + //Fill fixed file info + vs_fixedfileinfo fixed_info = {0}; + fixed_info.dwFileDateLS = file_info.get_file_date_ls(); + fixed_info.dwFileDateMS = file_info.get_file_date_ms(); + fixed_info.dwFileFlags = file_info.get_file_flags(); + fixed_info.dwFileFlagsMask = vs_ffi_fileflagsmask; + fixed_info.dwFileOS = file_info.get_file_os_raw(); + fixed_info.dwFileSubtype = file_info.get_file_subtype(); + fixed_info.dwFileType = file_info.get_file_type_raw(); + fixed_info.dwFileVersionLS = file_info.get_file_version_ls(); + fixed_info.dwFileVersionMS = file_info.get_file_version_ms(); + fixed_info.dwSignature = vs_ffi_signature; + fixed_info.dwStrucVersion = vs_ffi_strucversion; + fixed_info.dwProductVersionLS = file_info.get_product_version_ls(); + fixed_info.dwProductVersionMS = file_info.get_product_version_ms(); + + //Write root block and fixed file info to buffer + uint32_t data_ptr = 0; + memcpy(&version_data[data_ptr], &root_block, sizeof(version_info_block) - sizeof(uint16_t)); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + memcpy(&version_data[data_ptr], resource_version_info_reader::version_info_key.c_str(), (resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t)); + data_ptr += static_cast<uint32_t>((resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t)); + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + memcpy(&version_data[data_ptr], &fixed_info, sizeof(fixed_info)); + data_ptr += sizeof(fixed_info); + + //Write string values, if any + if(!string_values.empty()) + { + //Create string file info root block + version_info_block string_file_info_block = {0}; + string_file_info_block.Type = 1; //Block type is string + memcpy(&version_data[data_ptr], &string_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* string_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr1 = data_ptr; //Used to calculate string file info block length later + memcpy(&version_data[data_ptr], StringFileInfo, SizeofStringFileInfo); //Write block name + data_ptr += SizeofStringFileInfo; + + //Create string table root block (child of string file info) + version_info_block string_table_block = {0}; + string_table_block.Type = 1; //Block type is string + + for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) + { + const string_values_map& values = (*table_it).second; + + memcpy(&version_data[data_ptr], &string_table_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* string_table_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr2 = data_ptr; //Used to calculate string table block length later + uint32_t lang_key_length = static_cast<uint32_t>(((*table_it).first.length() + 1) * sizeof(uint16_t)); + +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*table_it).first.c_str(), lang_key_length); //Write block key +#else + { + u16string str(pe_utils::to_ucs2((*table_it).first)); + memcpy(&version_data[data_ptr], str.c_str(), lang_key_length); //Write block key + } +#endif + + data_ptr += lang_key_length; + //Align key if necessary + if((sizeof(uint16_t) * 3 + lang_key_length) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + + //Create string block (child of string table block) + version_info_block string_block = {0}; + string_block.Type = 1; //Block type is string + for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) + { + //Calculate value length and key length of string block + string_block.ValueLength = static_cast<uint16_t>((*it).second.length() + 1); + uint32_t key_length = static_cast<uint32_t>(((*it).first.length() + 1) * sizeof(uint16_t)); + //Calculate length of block + string_block.Length = static_cast<uint16_t>(pe_utils::align_up(sizeof(uint16_t) * 3 + key_length, sizeof(uint32_t)) + string_block.ValueLength * sizeof(uint16_t)); + + //Write string block + memcpy(&version_data[data_ptr], &string_block, sizeof(version_info_block) - sizeof(uint16_t)); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*it).first.c_str(), key_length); //Write block key +#else + { + u16string str(pe_utils::to_ucs2((*it).first)); + memcpy(&version_data[data_ptr], str.c_str(), key_length); //Write block key + } +#endif + + data_ptr += key_length; + //Align key if necessary + if((sizeof(uint16_t) * 3 + key_length) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + + //Write block data (value) +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*it).second.c_str(), string_block.ValueLength * sizeof(uint16_t)); +#else + { + u16string str(pe_utils::to_ucs2((*it).second)); + memcpy(&version_data[data_ptr], str.c_str(), string_block.ValueLength * sizeof(uint16_t)); + } +#endif + + data_ptr += string_block.ValueLength * 2; + //Align data if necessary + if((string_block.ValueLength * 2) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + } + + //Calculate string table and string file info blocks lengths + string_table_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); + } + + string_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); + } + + //If we have transactions + if(!translations.empty()) + { + //Create root var file info block + version_info_block var_file_info_block = {0}; + var_file_info_block.Type = 1; //Type of block is string + //Write block header + memcpy(&version_data[data_ptr], &var_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* var_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr1 = data_ptr; //Used to calculate var file info block length later + memcpy(&version_data[data_ptr], VarFileInfoAligned, SizeofVarFileInfoAligned); //Write block key (aligned) + data_ptr += SizeofVarFileInfoAligned; + + //Create root translation block (child of var file info block) + version_info_block translation_block = {0}; + //Write block header + memcpy(&version_data[data_ptr], &translation_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* translation_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr2 = data_ptr; //Used to calculate var file info block length later + memcpy(&version_data[data_ptr], TranslationAligned, SizeofTranslationAligned); //Write block key (aligned) + data_ptr += SizeofTranslationAligned; + + //Calculate translation block value length + translation_block_ptr->ValueLength = static_cast<uint16_t>(sizeof(uint16_t) * 2 * translations.size()); + + //Write translation values to block + for(translation_values_map::const_iterator it = translations.begin(); it != translations.end(); ++it) + { + uint16_t lang_id = (*it).first; //Language ID + uint16_t codepage_id = (*it).second; //Codepage ID + memcpy(&version_data[data_ptr], &lang_id, sizeof(lang_id)); + data_ptr += sizeof(lang_id); + memcpy(&version_data[data_ptr], &codepage_id, sizeof(codepage_id)); + data_ptr += sizeof(codepage_id); + } + + //Calculate Translation and VarFileInfo blocks lengths + translation_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); + var_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); + } + + //Add/replace version info resource + res_.add_resource(version_data, pe_resource_viewer::resource_version, 1, language, codepage, timestamp); +} + +//Removes version info by language (ID = 1) +bool resource_version_info_writer::remove_version_info(uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_version, 1, language); +} +} diff --git a/pe_lib/resource_version_info_writer.h b/pe_lib/resource_version_info_writer.h new file mode 100644 index 0000000..af50b44 --- /dev/null +++ b/pe_lib/resource_version_info_writer.h @@ -0,0 +1,31 @@ +#pragma once +#include "version_info_types.h" +#include "file_version_info.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_version_info_writer +{ +public: + resource_version_info_writer(pe_resource_manager& res); + + //Sets/replaces full version information: + //file_version_info: versions and file info + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + void set_version_info(const file_version_info& file_info, + const lang_string_values_map& string_values, + const translation_values_map& translations, + uint32_t language, + uint32_t codepage = 0, + uint32_t timestamp = 0); + + //Removes version info by language (ID = 1) + bool remove_version_info(uint32_t language); + +private: + pe_resource_manager& res_; +}; +} diff --git a/pe_lib/stdint_defs.h b/pe_lib/stdint_defs.h index 5d23274..6e32132 100644 --- a/pe_lib/stdint_defs.h +++ b/pe_lib/stdint_defs.h @@ -3,6 +3,8 @@ #if _MSC_VER < 1600 namespace pe_bliss { + //stdint.h definitions for MSVC 2008 and earlier, as + //it doesn't have them typedef signed char int8_t; typedef short int16_t; typedef int int32_t; diff --git a/pe_lib/utils.cpp b/pe_lib/utils.cpp new file mode 100644 index 0000000..99159c3 --- /dev/null +++ b/pe_lib/utils.cpp @@ -0,0 +1,86 @@ +#include <string.h> +#include "utils.h" +#include "pe_exception.h" + +#ifndef PE_BLISS_WINDOWS +#include <iconv.h> +#endif + +namespace pe_bliss +{ +const double pe_utils::log_2 = 1.44269504088896340736; //instead of using M_LOG2E + +//Returns stream size +std::streamoff pe_utils::get_file_size(std::istream& file) +{ + //Get old istream offset + std::streamoff old_offset = file.tellg(); + file.seekg(0, std::ios::end); + std::streamoff filesize = file.tellg(); + //Set old istream offset + file.seekg(old_offset); + return filesize; +} + +#ifndef PE_BLISS_WINDOWS +const u16string pe_utils::to_ucs2(const std::wstring& str) +{ + u16string ret; + if(str.empty()) + return ret; + + ret.resize(str.length()); + + iconv_t conv = iconv_open("UCS-2", "WCHAR_T"); + if(conv == reinterpret_cast<iconv_t>(-1)) + throw pe_exception("Error opening iconv", pe_exception::encoding_convertion_error); + + size_t inbytesleft = str.length() * sizeof(wchar_t); + size_t outbytesleft = ret.length() * sizeof(unicode16_t); + const wchar_t* in_pos = str.c_str(); + unicode16_t* out_pos = &ret[0]; + + size_t result = iconv(conv, const_cast<char**>(reinterpret_cast<const char**>(&in_pos)), &inbytesleft, reinterpret_cast<char**>(&out_pos), &outbytesleft); + iconv_close(conv); + + if(result == static_cast<size_t>(-1)) + throw pe_exception("Iconv error", pe_exception::encoding_convertion_error); + + return ret; +} + +const std::wstring pe_utils::from_ucs2(const u16string& str) +{ + std::wstring ret; + if(str.empty()) + return ret; + + ret.resize(str.length()); + + iconv_t conv = iconv_open("WCHAR_T", "UCS-2"); + if(conv == reinterpret_cast<iconv_t>(-1)) + throw pe_exception("Error opening iconv", pe_exception::encoding_convertion_error); + + size_t inbytesleft = str.length() * sizeof(unicode16_t); + size_t outbytesleft = ret.length() * sizeof(wchar_t); + const unicode16_t* in_pos = str.c_str(); + wchar_t* out_pos = &ret[0]; + + size_t result = iconv(conv, const_cast<char**>(reinterpret_cast<const char**>(&in_pos)), &inbytesleft, reinterpret_cast<char**>(&out_pos), &outbytesleft); + iconv_close(conv); + + if(result == static_cast<size_t>(-1)) + throw pe_exception("Iconv error", pe_exception::encoding_convertion_error); + + return ret; +} +#endif + +bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2) +{ + return guid1.Data1 == guid2.Data1 + && guid1.Data2 == guid2.Data2 + && guid1.Data3 == guid2.Data3 + && !memcmp(guid1.Data4, guid2.Data4, sizeof(guid1.Data4)); +} +} diff --git a/pe_lib/utils.h b/pe_lib/utils.h new file mode 100644 index 0000000..dc52fc4 --- /dev/null +++ b/pe_lib/utils.h @@ -0,0 +1,84 @@ +#pragma once +#include <istream> +#include <string> +#include "stdint_defs.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +class pe_utils +{ +public: + //Returns true if string "data" with maximum length "raw_length" is null-terminated + template<typename T> + static bool is_null_terminated(const T* data, size_t raw_length) + { + raw_length /= sizeof(T); + for(size_t l = 0; l < raw_length; l++) + { + if(data[l] == static_cast<T>(L'\0')) + return true; + } + + return false; + } + + //Helper template function to strip nullbytes in the end of string + template<typename T> + static void strip_nullbytes(std::basic_string<T>& str) + { + while(!*(str.end() - 1) && !str.empty()) + str.erase(str.length() - 1); + } + + //Helper function to determine if number is power of 2 + template<typename T> + static inline bool is_power_of_2(T x) + { + return !(x & (x - 1)); + } + + //Helper function to align number down + template<typename T> + static inline T align_down(T x, uint32_t align) + { + return x & ~(static_cast<T>(align) - 1); + } + + //Helper function to align number up + template<typename T> + static inline T align_up(T x, uint32_t align) + { + return (x & static_cast<T>(align - 1)) ? align_down(x, align) + static_cast<T>(align) : x; + } + + //Returns true if sum of two unsigned integers is safe (no overflow occurs) + static inline bool is_sum_safe(uint32_t a, uint32_t b) + { + return a <= static_cast<uint32_t>(-1) - b; + } + + //Two gigabytes value in bytes + static const uint32_t two_gb = 0x80000000; + static const uint32_t max_dword = 0xFFFFFFFF; + static const uint32_t max_word = 0x0000FFFF; + static const double log_2; //instead of using M_LOG2E + + //Returns stream size + static std::streamoff get_file_size(std::istream& file); + +#ifndef PE_BLISS_WINDOWS +public: + static const u16string to_ucs2(const std::wstring& str); + static const std::wstring from_ucs2(const u16string& str); +#endif + +private: + pe_utils(); + pe_utils(pe_utils&); + pe_utils& operator=(const pe_utils&); +}; + +//Windows GUID comparison +bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2); +} diff --git a/pe_lib/version_info_editor.cpp b/pe_lib/version_info_editor.cpp new file mode 100644 index 0000000..544d060 --- /dev/null +++ b/pe_lib/version_info_editor.cpp @@ -0,0 +1,163 @@ +#include <sstream> +#include <iomanip> +#include "version_info_types.h" +#include "version_info_editor.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ +//Default constructor +//strings - version info strings with charsets +//translations - version info translations map +version_info_editor::version_info_editor(lang_string_values_map& strings, translation_values_map& translations) + :version_info_viewer(strings, translations), + strings_edit_(strings), + translations_edit_(translations) +{} + +//Below functions have parameter translation +//If it's empty, the default language translation will be taken +//If there's no default language translation, the first one will be taken + +//Sets company name +void version_info_editor::set_company_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"CompanyName", value, translation); +} + +//Sets file description +void version_info_editor::set_file_description(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"FileDescription", value, translation); +} + +//Sets file version +void version_info_editor::set_file_version(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"FileVersion", value, translation); +} + +//Sets internal file name +void version_info_editor::set_internal_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"InternalName", value, translation); +} + +//Sets legal copyright +void version_info_editor::set_legal_copyright(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"LegalCopyright", value, translation); +} + +//Sets original file name +void version_info_editor::set_original_filename(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"OriginalFilename", value, translation); +} + +//Sets product name +void version_info_editor::set_product_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"ProductName", value, translation); +} + +//Sets product version +void version_info_editor::set_product_version(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"ProductVersion", value, translation); +} + +//Sets version info property value +//property_name - property name +//value - property value +//If translation does not exist, it will be added +//If property does not exist, it will be added +void version_info_editor::set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation) +{ + lang_string_values_map::iterator it = strings_edit_.begin(); + + if(translation.empty()) + { + //If no translation was specified + it = strings_edit_.find(default_language_translation); //Find default translation table + if(it == strings_edit_.end()) //If there's no default translation table, take the first one + { + it = strings_edit_.begin(); + if(it == strings_edit_.end()) //If there's no any translation table, add default one + { + it = strings_edit_.insert(std::make_pair(default_language_translation, string_values_map())).first; + //Also add it to translations list + add_translation(default_language_translation); + } + } + } + else + { + it = strings_edit_.find(translation); //Find specified translation table + if(it == strings_edit_.end()) //If there's no translation, add it + { + it = strings_edit_.insert(std::make_pair(translation, string_values_map())).first; + //Also add it to translations list + add_translation(translation); + } + } + + //Change value of the required property + ((*it).second)[property_name] = value; +} + +//Adds translation to translation list +void version_info_editor::add_translation(const std::wstring& translation) +{ + std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); + add_translation(translation_ids.first, translation_ids.second); +} + +void version_info_editor::add_translation(uint16_t language_id, uint16_t codepage_id) +{ + std::pair<translation_values_map::const_iterator, translation_values_map::const_iterator> + range(translations_edit_.equal_range(language_id)); + + //If translation already exists + for(translation_values_map::const_iterator it = range.first; it != range.second; ++it) + { + if((*it).second == codepage_id) + return; + } + + translations_edit_.insert(std::make_pair(language_id, codepage_id)); +} + +//Removes translation from translations and strings lists +void version_info_editor::remove_translation(const std::wstring& translation) +{ + std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); + remove_translation(translation_ids.first, translation_ids.second); +} + +void version_info_editor::remove_translation(uint16_t language_id, uint16_t codepage_id) +{ + { + //Erase string table (if exists) + std::wstringstream ss; + ss << std::hex + << std::setw(4) << std::setfill(L'0') << language_id + << std::setw(4) << std::setfill(L'0') << codepage_id; + + strings_edit_.erase(ss.str()); + } + + //Find and erase translation from translations table + std::pair<translation_values_map::iterator, translation_values_map::iterator> + it_pair = translations_edit_.equal_range(language_id); + + for(translation_values_map::iterator it = it_pair.first; it != it_pair.second; ++it) + { + if((*it).second == codepage_id) + { + translations_edit_.erase(it); + break; + } + } +} +} diff --git a/pe_lib/version_info_editor.h b/pe_lib/version_info_editor.h new file mode 100644 index 0000000..4b3ff85 --- /dev/null +++ b/pe_lib/version_info_editor.h @@ -0,0 +1,58 @@ +#pragma once +#include "version_info_types.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ + //Helper class to read and edit version information + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + class version_info_editor : public version_info_viewer + { + public: + //Default constructor + //strings - version info strings with charsets + //translations - version info translations map + version_info_editor(lang_string_values_map& strings, translation_values_map& translations); + + //Below functions have parameter translation + //If it's empty, the default language translation will be taken + //If there's no default language translation, the first one will be taken + + //Sets company name + void set_company_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets file description + void set_file_description(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets file version + void set_file_version(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets internal file name + void set_internal_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets legal copyright + void set_legal_copyright(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets original file name + void set_original_filename(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets product name + void set_product_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets product version + void set_product_version(const std::wstring& value, const std::wstring& translation = std::wstring()); + + //Sets version info property value + //property_name - property name + //value - property value + //If translation does not exist, it will be added to strings and translations lists + //If property does not exist, it will be added + void set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation = std::wstring()); + + //Adds translation to translation list + void add_translation(const std::wstring& translation); + void add_translation(uint16_t language_id, uint16_t codepage_id); + + //Removes translation from translations and strings lists + void remove_translation(const std::wstring& translation); + void remove_translation(uint16_t language_id, uint16_t codepage_id); + + private: + lang_string_values_map& strings_edit_; + translation_values_map& translations_edit_; + }; +} diff --git a/pe_lib/version_info_types.h b/pe_lib/version_info_types.h new file mode 100644 index 0000000..148e0b1 --- /dev/null +++ b/pe_lib/version_info_types.h @@ -0,0 +1,17 @@ +#pragma once +#include <map> +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ + //Typedef for version info functions: Name - Value + typedef std::map<std::wstring, std::wstring> string_values_map; + //Typedef for version info functions: Language string - String Values Map + //Language String consists of LangID and CharsetID + //E.g. 041904b0 for Russian UNICODE, 040004b0 for Process Default Language UNICODE + typedef std::map<std::wstring, string_values_map> lang_string_values_map; + + //Typedef for version info functions: Language - Character Set + typedef std::multimap<uint16_t, uint16_t> translation_values_map; +} diff --git a/pe_lib/version_info_viewer.cpp b/pe_lib/version_info_viewer.cpp new file mode 100644 index 0000000..51b52bd --- /dev/null +++ b/pe_lib/version_info_viewer.cpp @@ -0,0 +1,159 @@ +#include <iomanip> +#include <sstream> +#include "pe_exception.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ +//Default process language, UNICODE +const std::wstring version_info_viewer::default_language_translation(L"041904b0"); + +//Default constructor +//strings - version info strings with charsets +//translations - version info translations map +version_info_viewer::version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations) + :strings_(strings), translations_(translations) +{} + +//Below functions have parameter translation +//If it's empty, the default language translation will be taken +//If there's no default language translation, the first one will be taken + +//Returns company name +const std::wstring version_info_viewer::get_company_name(const std::wstring& translation) const +{ + return get_property(L"CompanyName", translation); +} + +//Returns file description +const std::wstring version_info_viewer::get_file_description(const std::wstring& translation) const +{ + return get_property(L"FileDescription", translation); +} + +//Returns file version +const std::wstring version_info_viewer::get_file_version(const std::wstring& translation) const +{ + return get_property(L"FileVersion", translation); +} + +//Returns internal file name +const std::wstring version_info_viewer::get_internal_name(const std::wstring& translation) const +{ + return get_property(L"InternalName", translation); +} + +//Returns legal copyright +const std::wstring version_info_viewer::get_legal_copyright(const std::wstring& translation) const +{ + return get_property(L"LegalCopyright", translation); +} + +//Returns original file name +const std::wstring version_info_viewer::get_original_filename(const std::wstring& translation) const +{ + return get_property(L"OriginalFilename", translation); +} + +//Returns product name +const std::wstring version_info_viewer::get_product_name(const std::wstring& translation) const +{ + return get_property(L"ProductName", translation); +} + +//Returns product version +const std::wstring version_info_viewer::get_product_version(const std::wstring& translation) const +{ + return get_property(L"ProductVersion", translation); +} + +//Returns list of translations in string representation +const version_info_viewer::translation_list version_info_viewer::get_translation_list() const +{ + translation_list ret; + + //Enumerate all translations + for(translation_values_map::const_iterator it = translations_.begin(); it != translations_.end(); ++it) + { + //Create string representation of translation value + std::wstringstream ss; + ss << std::hex + << std::setw(4) << std::setfill(L'0') << (*it).first + << std::setw(4) << std::setfill(L'0') << (*it).second; + + //Save it + ret.push_back(ss.str()); + } + + return ret; +} + +//Returns version info property value +//property_name - required property name +//If throw_if_absent = true, will throw exception if property does not exist +//If throw_if_absent = false, will return empty string if property does not exist +const std::wstring version_info_viewer::get_property(const std::wstring& property_name, const std::wstring& translation, bool throw_if_absent) const +{ + std::wstring ret; + + //If there're no strings + if(strings_.empty()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + + lang_string_values_map::const_iterator it = strings_.begin(); + + if(translation.empty()) + { + //If no translation was specified + it = strings_.find(default_language_translation); //Find default translation table + if(it == strings_.end()) //If there's no default translation table, take the first one + it = strings_.begin(); + } + else + { + it = strings_.find(translation); //Find specified translation table + if(it == strings_.end()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + } + + //Find value of the required property + string_values_map::const_iterator str_it = (*it).second.find(property_name); + + if(str_it == (*it).second.end()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + + ret = (*str_it).second; + + return ret; +} + +//Converts translation HEX-string to pair of language ID and codepage ID +const version_info_viewer::translation_pair version_info_viewer::translation_from_string(const std::wstring& translation) +{ + uint32_t translation_id = 0; + + { + //Convert string to DWORD + std::wstringstream ss; + ss << std::hex << translation; + ss >> translation_id; + } + + return std::make_pair(static_cast<uint16_t>(translation_id >> 16), static_cast<uint16_t>(translation_id & 0xFFFF)); +} +} diff --git a/pe_lib/version_info_viewer.h b/pe_lib/version_info_viewer.h new file mode 100644 index 0000000..f4163eb --- /dev/null +++ b/pe_lib/version_info_viewer.h @@ -0,0 +1,68 @@ +#pragma once +#include <map> +#include <vector> +#include <string> +#include "pe_resource_viewer.h" +#include "pe_structures.h" +#include "version_info_types.h" + +namespace pe_bliss +{ +//Helper class to read version information +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +class version_info_viewer +{ +public: + //Useful typedefs + typedef std::pair<uint16_t, uint16_t> translation_pair; + typedef std::vector<std::wstring> translation_list; + +public: + //Default constructor + //strings - version info strings with charsets + //translations - version info translations map + version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations); + + //Below functions have parameter translation + //If it's empty, the default language translation will be taken + //If there's no default language translation, the first one will be taken + + //Returns company name + const std::wstring get_company_name(const std::wstring& translation = std::wstring()) const; + //Returns file description + const std::wstring get_file_description(const std::wstring& translation = std::wstring()) const; + //Returns file version + const std::wstring get_file_version(const std::wstring& translation = std::wstring()) const; + //Returns internal file name + const std::wstring get_internal_name(const std::wstring& translation = std::wstring()) const; + //Returns legal copyright + const std::wstring get_legal_copyright(const std::wstring& translation = std::wstring()) const; + //Returns original file name + const std::wstring get_original_filename(const std::wstring& translation = std::wstring()) const; + //Returns product name + const std::wstring get_product_name(const std::wstring& translation = std::wstring()) const; + //Returns product version + const std::wstring get_product_version(const std::wstring& translation = std::wstring()) const; + + //Returns list of translations in string representation + const translation_list get_translation_list() const; + + //Returns version info property value + //property_name - required property name + //If throw_if_absent = true, will throw exception if property does not exist + //If throw_if_absent = false, will return empty string if property does not exist + const std::wstring get_property(const std::wstring& property_name, const std::wstring& translation = std::wstring(), bool throw_if_absent = false) const; + + //Converts translation HEX-string to pair of language ID and codepage ID + static const translation_pair translation_from_string(const std::wstring& translation); + +public: + //Default process language, UNICODE + static const std::wstring default_language_translation; + +private: + const lang_string_values_map& strings_; + const translation_values_map& translations_; +}; +} diff --git a/samples/address_convertions/address_convertions.vcproj b/samples/address_convertions/address_convertions.vcproj index 9e5d276..4328c30 100644 --- a/samples/address_convertions/address_convertions.vcproj +++ b/samples/address_convertions/address_convertions.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/address_convertions/address_convertions.vcxproj b/samples/address_convertions/address_convertions.vcxproj index 2b388ea..d214b76 100644 --- a/samples/address_convertions/address_convertions.vcxproj +++ b/samples/address_convertions/address_convertions.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> </ClCompile> diff --git a/samples/address_convertions/main.cpp b/samples/address_convertions/main.cpp index 02045f9..2065a97 100644 --- a/samples/address_convertions/main.cpp +++ b/samples/address_convertions/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,20 +27,20 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Получаем ÑпиÑок Ñекций std::cout << "Reading PE sections..." << std::hex << std::showbase << std::endl << std::endl; - const pe_base::section_list sections = image->get_image_sections(); + const section_list sections = image.get_image_sections(); //ПеречиÑлÑем Ñекции и выводим информацию о них - for(pe_base::section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) { - const pe_base::section& s = *it; //Ð¡ÐµÐºÑ†Ð¸Ñ + const section& s = *it; //Ð¡ÐµÐºÑ†Ð¸Ñ std::cout << "Section [" << s.get_name() << "]" << std::endl //Ð˜Ð¼Ñ Ñекции << " -> RVA: " << s.get_virtual_address() << std::endl //Виртуальный Ð°Ð´Ñ€ÐµÑ (RVA) - << " -> VA: " << image->rva_to_va_64(s.get_virtual_address()) << std::endl //Виртуальный Ð°Ð´Ñ€ÐµÑ (VA) - << " -> File offset: " << image->rva_to_file_offset(s.get_virtual_address()) //Файловое Ñмещение Ñекции, вычиÑленное из ее RVA + << " -> VA: " << image.rva_to_va_64(s.get_virtual_address()) << std::endl //Виртуальный Ð°Ð´Ñ€ÐµÑ (VA) + << " -> File offset: " << image.rva_to_file_offset(s.get_virtual_address()) //Файловое Ñмещение Ñекции, вычиÑленное из ее RVA << std::endl << std::endl; } } diff --git a/samples/basic_dotnet_viewer/basic_dotnet_viewer.vcproj b/samples/basic_dotnet_viewer/basic_dotnet_viewer.vcproj index 4d09761..619115e 100644 --- a/samples/basic_dotnet_viewer/basic_dotnet_viewer.vcproj +++ b/samples/basic_dotnet_viewer/basic_dotnet_viewer.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/basic_dotnet_viewer/basic_dotnet_viewer.vcxproj b/samples/basic_dotnet_viewer/basic_dotnet_viewer.vcxproj index 61051dc..bf1d2ee 100644 --- a/samples/basic_dotnet_viewer/basic_dotnet_viewer.vcxproj +++ b/samples/basic_dotnet_viewer/basic_dotnet_viewer.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/basic_dotnet_viewer/main.cpp b/samples/basic_dotnet_viewer/main.cpp index 6dd9630..8e28b83 100644 --- a/samples/basic_dotnet_viewer/main.cpp +++ b/samples/basic_dotnet_viewer/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,10 +27,10 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //ЕÑли образ не .NET, выходим - if(!image->is_dotnet()) + if(!image.is_dotnet()) { std::cout << "Image is not .NET" << std::endl; return 0; @@ -39,7 +39,7 @@ int main(int argc, char* argv[]) std::cout << "Reading basic dotnet info..." << std::hex << std::showbase << std::endl << std::endl; //Получаем .NET-заголовок PE-файла - const pe_base::basic_dotnet_info info(image->get_basic_dotnet_info()); + const basic_dotnet_info info(get_basic_dotnet_info(image)); //Выводим некоторую информацию std::cout << "Major runtime version: " << info.get_major_runtime_version() << std::endl //ВерÑÐ¸Ñ Ñ€Ð°Ð½Ñ‚Ð°Ð¹Ð¼Ð° diff --git a/samples/basic_info_viewer/basic_info_viewer.vcproj b/samples/basic_info_viewer/basic_info_viewer.vcproj index e7a2069..99224ee 100644 --- a/samples/basic_info_viewer/basic_info_viewer.vcproj +++ b/samples/basic_info_viewer/basic_info_viewer.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/basic_info_viewer/basic_info_viewer.vcxproj b/samples/basic_info_viewer/basic_info_viewer.vcxproj index 5a42521..e5e67c9 100644 --- a/samples/basic_info_viewer/basic_info_viewer.vcxproj +++ b/samples/basic_info_viewer/basic_info_viewer.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> </ClCompile> diff --git a/samples/basic_info_viewer/main.cpp b/samples/basic_info_viewer/main.cpp index b820c38..8270896 100644 --- a/samples/basic_info_viewer/main.cpp +++ b/samples/basic_info_viewer/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,49 +27,49 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Фабрика автоматичеÑки получает тип PE-файла и Ñоздает ÑкземплÑÑ€ нужного клаÑÑа, //но можно получить тип PE-файла и вручную, воÑпользовавшиÑÑŒ одной из перегрузок функции get_pe_type //ЗдеÑÑŒ мы проÑто выведем уже извеÑтный тип PE-файла: - std::cout << "PE file type: " << (image->get_pe_type() == pe_base::pe_type_32 ? "PE32 (PE)" : "PE64 (PE+)") << std::endl; + std::cout << "PE file type: " << (image.get_pe_type() == pe_type_32 ? "PE32 (PE)" : "PE64 (PE+)") << std::endl; //ВычиÑлим контрольную Ñумму PE-файла - std::cout << "Calculated checksum: "<< std::hex << std::showbase << pe_base::calculate_checksum(pe_file) << std::endl; + std::cout << "Calculated checksum: "<< std::hex << std::showbase << calculate_checksum(pe_file) << std::endl; //Выведем контрольную Ñумму из заголовка файла (Ð´Ð»Ñ Ð½Ðµ-драйверов она обычно равна 0) - std::cout << "Stored checksum: " << image->get_checksum() << std::endl; + std::cout << "Stored checksum: " << image.get_checksum() << std::endl; //Выведем характериÑтики PE-файла - std::cout << "Characteristics: " << image->get_characteristics() << std::endl; + std::cout << "Characteristics: " << image.get_characteristics() << std::endl; //Выведем Ð°Ð´Ñ€ÐµÑ Ñ‚Ð¾Ñ‡ÐºÐ¸ входа - std::cout << "Entry point: " << image->get_ep() << std::endl; + std::cout << "Entry point: " << image.get_ep() << std::endl; //Выведем выравнивание - std::cout << "File alignment: " << image->get_file_alignment() << std::endl; - std::cout << "Section alignment: " << image->get_section_alignment() << std::endl; + std::cout << "File alignment: " << image.get_file_alignment() << std::endl; + std::cout << "Section alignment: " << image.get_section_alignment() << std::endl; //Выведем базу образа в 64-битном виде (универÑально Ð´Ð»Ñ PE и PE+) - std::cout << "Image base: " << image->get_image_base_64() << std::endl; + std::cout << "Image base: " << image.get_image_base_64() << std::endl; //Выведем подÑиÑтему - std::cout << "Subsystem: " << image->get_subsystem() << std::endl; - std::cout << "Is console: " << (image->is_console() ? "YES" : "NO") << std::endl; - std::cout << "Is windows GUI: " << (image->is_gui() ? "YES" : "NO") << std::endl; + std::cout << "Subsystem: " << image.get_subsystem() << std::endl; + std::cout << "Is console: " << (image.is_console() ? "YES" : "NO") << std::endl; + std::cout << "Is windows GUI: " << (image.is_gui() ? "YES" : "NO") << std::endl; //Выведем, какие директории еÑÑ‚ÑŒ у файла - std::cout << "Has bound import: " << (image->has_bound_import() ? "YES" : "NO") << std::endl; - std::cout << "Has config: " << (image->has_config() ? "YES" : "NO") << std::endl; - std::cout << "Has debug: " << (image->has_debug() ? "YES" : "NO") << std::endl; - std::cout << "Has delay import: " << (image->has_delay_import() ? "YES" : "NO") << std::endl; - std::cout << "Has exception directory: " << (image->has_exception_directory() ? "YES" : "NO") << std::endl; - std::cout << "Has exports: " << (image->has_exports() ? "YES" : "NO") << std::endl; - std::cout << "Has imports: " << (image->has_imports() ? "YES" : "NO") << std::endl; - std::cout << "Has reloc: " << (image->has_reloc() ? "YES" : "NO") << std::endl; - std::cout << "Has resources: " << (image->has_resources() ? "YES" : "NO") << std::endl; - std::cout << "Has security: " << (image->has_security() ? "YES" : "NO") << std::endl; - std::cout << "Has tls: " << (image->has_tls() ? "YES" : "NO") << std::endl; - std::cout << "Is .NET: " << (image->is_dotnet() ? "YES" : "NO") << std::endl; + std::cout << "Has bound import: " << (image.has_bound_import() ? "YES" : "NO") << std::endl; + std::cout << "Has config: " << (image.has_config() ? "YES" : "NO") << std::endl; + std::cout << "Has debug: " << (image.has_debug() ? "YES" : "NO") << std::endl; + std::cout << "Has delay import: " << (image.has_delay_import() ? "YES" : "NO") << std::endl; + std::cout << "Has exception directory: " << (image.has_exception_directory() ? "YES" : "NO") << std::endl; + std::cout << "Has exports: " << (image.has_exports() ? "YES" : "NO") << std::endl; + std::cout << "Has imports: " << (image.has_imports() ? "YES" : "NO") << std::endl; + std::cout << "Has reloc: " << (image.has_reloc() ? "YES" : "NO") << std::endl; + std::cout << "Has resources: " << (image.has_resources() ? "YES" : "NO") << std::endl; + std::cout << "Has security: " << (image.has_security() ? "YES" : "NO") << std::endl; + std::cout << "Has tls: " << (image.has_tls() ? "YES" : "NO") << std::endl; + std::cout << "Is .NET: " << (image.is_dotnet() ? "YES" : "NO") << std::endl; } catch(const pe_exception& e) { diff --git a/samples/bound_import_reader/bound_import_reader.vcproj b/samples/bound_import_reader/bound_import_reader.vcproj index b611d51..bdfd549 100644 --- a/samples/bound_import_reader/bound_import_reader.vcproj +++ b/samples/bound_import_reader/bound_import_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/bound_import_reader/bound_import_reader.vcxproj b/samples/bound_import_reader/bound_import_reader.vcxproj index cd56e00..cd21e75 100644 --- a/samples/bound_import_reader/bound_import_reader.vcxproj +++ b/samples/bound_import_reader/bound_import_reader.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/bound_import_reader/main.cpp b/samples/bound_import_reader/main.cpp index b0808ad..13a49d3 100644 --- a/samples/bound_import_reader/main.cpp +++ b/samples/bound_import_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,10 +27,10 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Проверим, еÑÑ‚ÑŒ ли привÑзанный импорт у PE-файла - if(!image->has_bound_import()) + if(!image.has_bound_import()) { std::cout << "Image has no bound import" << std::endl; return 0; @@ -39,18 +39,18 @@ int main(int argc, char* argv[]) std::cout << "Reading PE bound import..." << std::hex << std::showbase << std::endl << std::endl; //Получаем информацию о привÑзанном импорте - const pe_base::bound_import_module_list modules = image->get_bound_import_module_list(); + const bound_import_module_list modules(get_bound_import_module_list(image)); //Выведем импортируемые модули и форварды - for(pe_base::bound_import_module_list::const_iterator it = modules.begin(); it != modules.end(); ++it) + for(bound_import_module_list::const_iterator it = modules.begin(); it != modules.end(); ++it) { - const pe_base::bound_import& import = *it; //Ð˜Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¸Ñ€ÑƒÐµÐ¼Ð°Ñ Ð±Ð¸Ð±Ð»Ð¸Ð¾Ñ‚ÐµÐºÐ° + const bound_import& import = *it; //Ð˜Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¸Ñ€ÑƒÐµÐ¼Ð°Ñ Ð±Ð¸Ð±Ð»Ð¸Ð¾Ñ‚ÐµÐºÐ° std::cout << "Module: " << import.get_module_name() << std::endl //Ð˜Ð¼Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ << "Timestamp: " << import.get_timestamp() << std::endl; //Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð¼ÐµÑ‚ÐºÐ° //ПеречиÑлим форварды Ð´Ð»Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ - модули, на которые ÑÑылаетÑÑ Ñтот: - const pe_base::bound_import::ref_list& refs = import.get_module_ref_list(); - for(pe_base::bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) + const bound_import::ref_list& refs = import.get_module_ref_list(); + for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) { std::cout << " -> Module: " << (*ref_it).get_module_name() << std::endl //Ð˜Ð¼Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ, на который ÑÑылаетÑÑ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑкий модуль << " -> Timestamp: " << (*ref_it).get_timestamp() << std::endl; //Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð¼ÐµÑ‚ÐºÐ° diff --git a/samples/debug_info_reader/debug_info_reader.vcproj b/samples/debug_info_reader/debug_info_reader.vcproj index c60878b..6385211 100644 --- a/samples/debug_info_reader/debug_info_reader.vcproj +++ b/samples/debug_info_reader/debug_info_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/debug_info_reader/debug_info_reader.vcxproj b/samples/debug_info_reader/debug_info_reader.vcxproj index 6d232d9..7e0f283 100644 --- a/samples/debug_info_reader/debug_info_reader.vcxproj +++ b/samples/debug_info_reader/debug_info_reader.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/debug_info_reader/main.cpp b/samples/debug_info_reader/main.cpp index 03ce50a..a8513f7 100644 --- a/samples/debug_info_reader/main.cpp +++ b/samples/debug_info_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,10 +27,10 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Проверим, еÑÑ‚ÑŒ ли Ð¾Ñ‚Ð»Ð°Ð´Ð¾Ñ‡Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ñƒ файла - if(!image->has_debug()) + if(!image.has_debug()) { std::cout << "Image has no debug information" << std::endl; return 0; @@ -39,54 +39,54 @@ int main(int argc, char* argv[]) std::cout << "Reading PE debug information..." << std::hex << std::showbase << std::endl << std::endl; //Получаем отладочную информацию, находÑщуюÑÑ Ð² PE-файле - const pe_base::debug_info_list info_list = image->get_debug_information(); + const debug_info_list info_list(get_debug_information(image)); //ПеречиÑолÑем вÑе отладочные запиÑи - for(pe_base::debug_info_list::const_iterator it = info_list.begin(); it != info_list.end(); ++it) + for(debug_info_list::const_iterator it = info_list.begin(); it != info_list.end(); ++it) { - const pe_base::debug_info& info = *it; + const debug_info& info = *it; //Выведем тип отладочной инфоÑ€Ð¼Ð°Ñ†Ð¸Ð¸ std::cout << "Debug info type: "; switch(info.get_type()) { - case pe_base::debug_info::debug_type_borland: + case debug_info::debug_type_borland: std::cout << "Borland"; break; - case pe_base::debug_info::debug_type_clsid: + case debug_info::debug_type_clsid: std::cout << "CLSID"; break; - case pe_base::debug_info::debug_type_codeview: + case debug_info::debug_type_codeview: std::cout << "CodeView"; break; - case pe_base::debug_info::debug_type_coff: + case debug_info::debug_type_coff: std::cout << "COFF"; break; - case pe_base::debug_info::debug_type_exception: + case debug_info::debug_type_exception: std::cout << "Exception"; break; - case pe_base::debug_info::debug_type_fixup: + case debug_info::debug_type_fixup: std::cout << "Fixup"; break; - case pe_base::debug_info::debug_type_fpo: + case debug_info::debug_type_fpo: std::cout << "FPO"; break; - case pe_base::debug_info::debug_type_misc: + case debug_info::debug_type_misc: std::cout << "Misc"; break; - case pe_base::debug_info::debug_type_omap_from_src: + case debug_info::debug_type_omap_from_src: std::cout << "OMAP from src"; break; - case pe_base::debug_info::debug_type_omap_to_src: + case debug_info::debug_type_omap_to_src: std::cout << "OMAP to src"; break; @@ -101,28 +101,28 @@ int main(int argc, char* argv[]) //Получим дополнительную информацию, еÑли Ñ‚Ð°ÐºÐ¾Ð²Ð°Ñ Ð¸Ð¼ÐµÐµÑ‚ÑÑ switch(info.get_advanced_info_type()) { - case pe_base::debug_info::advanced_info_pdb_7_0: + case debug_info::advanced_info_pdb_7_0: { std::cout << "Advanced info - PDB 7.0" << std::endl; //PDB 7.0 - pe_base::pdb_7_0_info advanced = info.get_advanced_debug_info<pe_base::pdb_7_0_info>(); + pdb_7_0_info advanced = info.get_advanced_debug_info<pdb_7_0_info>(); std::cout << "PDB file name: " << advanced.get_pdb_file_name() << std::endl; //Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° PDB std::cout << "Age: " << advanced.get_age() << std::endl; //ВозраÑÑ‚ (билд) } break; - case pe_base::debug_info::advanced_info_pdb_2_0: + case debug_info::advanced_info_pdb_2_0: { std::cout << "Advanced info - PDB 2.0" << std::endl; //PDB 2.0 - pe_base::pdb_2_0_info advanced = info.get_advanced_debug_info<pe_base::pdb_2_0_info>(); + pdb_2_0_info advanced = info.get_advanced_debug_info<pdb_2_0_info>(); std::cout << "PDB file name: " << advanced.get_pdb_file_name() << std::endl; //Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° PDB std::cout << "Age: " << advanced.get_age() << std::endl; //ВозраÑÑ‚ (билд) } break; - case pe_base::debug_info::advanced_info_misc: + case debug_info::advanced_info_misc: { std::cout << "Advanced info - Misc" << std::endl; //Misc - pe_base::misc_debug_info advanced = info.get_advanced_debug_info<pe_base::misc_debug_info>(); + misc_debug_info advanced = info.get_advanced_debug_info<misc_debug_info>(); std::cout << "Advanced data is EXE name: " << (advanced.is_exe_name() ? "YES" : "NO") << std::endl; //ЕÑли данные в Ñтруктуре - Ð¸Ð¼Ñ EXE-файла //Выведем Ñтроковые данные @@ -133,10 +133,10 @@ int main(int argc, char* argv[]) } break; - case pe_base::debug_info::advanced_info_coff: + case debug_info::advanced_info_coff: { std::cout << "Advanced info - COFF" << std::endl; //COFF - pe_base::coff_debug_info advanced = info.get_advanced_debug_info<pe_base::coff_debug_info>(); + coff_debug_info advanced = info.get_advanced_debug_info<coff_debug_info>(); std::cout << "LVA to first line number: " << advanced.get_lva_to_first_line_number() << std::endl; //ÐÐ´Ñ€ÐµÑ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ Ñлемента в маÑÑиве номеров Ñтрок std::cout << "LVA to first symbol: " << advanced.get_lva_to_first_symbol() << std::endl; //ÐÐ´Ñ€ÐµÑ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ Ñлемента в маÑÑиве Ñимволов std::cout << "Number of line numbers: " << advanced.get_number_of_line_numbers() << std::endl; //КоличеÑтво номеров Ñтрок @@ -149,11 +149,11 @@ int main(int argc, char* argv[]) std::cout << std::endl << "Symbol list:" << std::endl; //Получим ÑпиÑок Ñимволов - const pe_base::coff_debug_info::coff_symbols_list& symbols = advanced.get_symbols(); - for(pe_base::coff_debug_info::coff_symbols_list::const_iterator symbol_it = symbols.begin(); symbol_it != symbols.end(); ++symbol_it) + const coff_debug_info::coff_symbols_list& symbols = advanced.get_symbols(); + for(coff_debug_info::coff_symbols_list::const_iterator symbol_it = symbols.begin(); symbol_it != symbols.end(); ++symbol_it) { //Выведем информацию об отладочных Ñимволах - const pe_base::coff_debug_info::coff_symbol& symbol = *symbol_it; //Отладочный Ñимвол + const coff_debug_info::coff_symbol& symbol = *symbol_it; //Отладочный Ñимвол std::cout << "Index: " << symbol.get_index() << std::endl << "RVA: " << symbol.get_rva() << std::endl << "Section number: " << symbol.get_section_number() << std::endl @@ -165,11 +165,11 @@ int main(int argc, char* argv[]) } break; - case pe_base::debug_info::advanced_info_codeview_4_0: + case debug_info::advanced_info_codeview_4_0: std::cout << "Advanced info - CodeView 4.0" << std::endl; //CodeView 4.0 break; - case pe_base::debug_info::advanced_info_codeview_5_0: + case debug_info::advanced_info_codeview_5_0: std::cout << "Advanced info - CodeView 5.0" << std::endl; //CodeView 5.0 break; diff --git a/samples/entropy_calculator/entropy_calculator.vcproj b/samples/entropy_calculator/entropy_calculator.vcproj index 0c15b8a..a092c2e 100644 --- a/samples/entropy_calculator/entropy_calculator.vcproj +++ b/samples/entropy_calculator/entropy_calculator.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/entropy_calculator/entropy_calculator.vcxproj b/samples/entropy_calculator/entropy_calculator.vcxproj index 5bdf4ce..116cf88 100644 --- a/samples/entropy_calculator/entropy_calculator.vcxproj +++ b/samples/entropy_calculator/entropy_calculator.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> </ClCompile> diff --git a/samples/entropy_calculator/main.cpp b/samples/entropy_calculator/main.cpp index 34d9610..88b550c 100644 --- a/samples/entropy_calculator/main.cpp +++ b/samples/entropy_calculator/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -28,19 +28,19 @@ int main(int argc, char* argv[]) try { //Считаем Ñнтропию файла - std::cout << "File entropy: " << pe_base::calculate_entropy(pe_file) << std::endl; + std::cout << "File entropy: " << entropy_calculator::calculate_entropy(pe_file) << std::endl; //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); - std::cout << "Sections entropy: " << image->calculate_entropy() << std::endl; //Считаем Ñнтропию вÑех Ñекций + std::cout << "Sections entropy: " << entropy_calculator::calculate_entropy(image) << std::endl; //Считаем Ñнтропию вÑех Ñекций //ПеречиÑлÑем Ñекции и Ñчитаем их Ñнтропию по отдельноÑти - const pe_base::section_list sections = image->get_image_sections(); - for(pe_base::section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + const section_list sections = image.get_image_sections(); + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) { if(!(*it).empty()) //ЕÑли ÑÐµÐºÑ†Ð¸Ñ Ð½Ðµ пуÑта - поÑчитаем ее Ñнтропию - std::cout << "Section [" << (*it).get_name() << "] entropy: " << pe_base::calculate_entropy(*it) << std::endl; + std::cout << "Section [" << (*it).get_name() << "] entropy: " << entropy_calculator::calculate_entropy(*it) << std::endl; } } catch(const pe_exception& e) diff --git a/samples/exception_dir_reader/exception_dir_reader.vcproj b/samples/exception_dir_reader/exception_dir_reader.vcproj index d2002ec..9c16412 100644 --- a/samples/exception_dir_reader/exception_dir_reader.vcproj +++ b/samples/exception_dir_reader/exception_dir_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/exception_dir_reader/exception_dir_reader.vcxproj b/samples/exception_dir_reader/exception_dir_reader.vcxproj index db41f56..4d7a662 100644 --- a/samples/exception_dir_reader/exception_dir_reader.vcxproj +++ b/samples/exception_dir_reader/exception_dir_reader.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/exception_dir_reader/main.cpp b/samples/exception_dir_reader/main.cpp index c9fffbc..8290692 100644 --- a/samples/exception_dir_reader/main.cpp +++ b/samples/exception_dir_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -28,10 +28,10 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Проверим, еÑÑ‚ÑŒ ли Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ об иÑключениÑÑ… у PE-файла - if(!image->has_exception_directory()) + if(!image.has_exception_directory()) { std::cout << "Image has no exception directory" << std::endl; return 0; @@ -40,13 +40,13 @@ int main(int argc, char* argv[]) std::cout << "Reading exception directory..." << std::hex << std::showbase << std::endl << std::endl; //Получаем информацию из exception directory - const pe_base::exception_entry_list info = image->get_exception_directory_data(); + const exception_entry_list info(get_exception_directory_data(image)); //Выведем запиÑи из exception directory //Подробное опиÑание вÑех Ñтих Ñтруктур еÑÑ‚ÑŒ в MSDN - for(pe_base::exception_entry_list::const_iterator it = info.begin(); it != info.end(); ++it) + for(exception_entry_list::const_iterator it = info.begin(); it != info.end(); ++it) { - const pe_base::exception_entry& entry = *it; //ЗапиÑÑŒ из таблицы + const exception_entry& entry = *it; //ЗапиÑÑŒ из таблицы //Выведем информацию std::cout << "Addresses: [" << entry.get_begin_address() << ":" << entry.get_end_address() << "]:" << std::endl diff --git a/samples/export_adder/export_adder.vcproj b/samples/export_adder/export_adder.vcproj index bfda59c..15cb2d3 100644 --- a/samples/export_adder/export_adder.vcproj +++ b/samples/export_adder/export_adder.vcproj @@ -118,7 +118,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/export_adder/export_adder.vcxproj b/samples/export_adder/export_adder.vcxproj index acd1f73..fc3e9bf 100644 --- a/samples/export_adder/export_adder.vcxproj +++ b/samples/export_adder/export_adder.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/export_adder/main.cpp b/samples/export_adder/main.cpp index fd60a6c..b488454 100644 --- a/samples/export_adder/main.cpp +++ b/samples/export_adder/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,17 +27,17 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Получим ÑпиÑок ÑкÑпортируемых функций и информацию об ÑкÑпорте - pe_base::export_info info; - pe_base::exported_functions_list exports; + export_info info; + exported_functions_list exports; //ЕÑли ÑкÑпортов у файла нет, Ñтот вызов броÑит иÑключение, но Ñто не значит, что мы //не можем Ñоздать таблицу ÑкÑпортов Ñ Ð½ÑƒÐ»Ñ try { - exports = image->get_exported_functions(info); + exports = get_exported_functions(image, info); } catch(const pe_exception&) { @@ -48,13 +48,13 @@ int main(int argc, char* argv[]) } //Создаем новую ÑкÑпортируемую функцию - pe_base::exported_function func; + exported_function func; func.set_name("SuperKernelCall"); //Ð˜Ð¼Ñ ÑкÑпортируемой функции func.set_rva(0x123); //ОтноÑительный Ð°Ð´Ñ€ÐµÑ Ñ‚Ð¾Ñ‡ÐºÐ¸ входа ÑкÑпортируемой функции (некорректный, чиÑто Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÑ€Ð°) //Ðеобходимо вычиÑлить ординал функции, которую мы добавлÑем, чтобы не было повторных //Ð”Ð»Ñ Ñтого еÑÑ‚ÑŒ вÑÐ¿Ð¾Ð¼Ð¾Ð³Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ - func.set_ordinal(pe_base::get_export_ordinal_limits(exports).second + 1); //Сделаем наш ординал = макÑимальный ординал Ñреди ÑущеÑтвующих ÑкÑпортов + 1 + func.set_ordinal(get_export_ordinal_limits(exports).second + 1); //Сделаем наш ординал = макÑимальный ординал Ñреди ÑущеÑтвующих ÑкÑпортов + 1 exports.push_back(func); //Добавим функцию к ÑкÑпортам //Можно редактировать и ÑущеÑтвующие ÑкÑпорты @@ -63,13 +63,13 @@ int main(int argc, char* argv[]) //Она будет иметь больший размер, чем до нашего редактированиÑ, //поÑтому запишем ее в новую Ñекцию, чтобы вÑе помеÑтилоÑÑŒ //(мы не можем раÑширÑÑ‚ÑŒ ÑущеÑтвующие Ñекции, еÑли только ÑÐµÐºÑ†Ð¸Ñ Ð½Ðµ в Ñамом конце файла) - pe_base::section new_exports; + section new_exports; new_exports.get_raw_data().resize(1); //Мы не можем добавлÑÑ‚ÑŒ пуÑтые Ñекции, поÑтому пуÑÑ‚ÑŒ у нее будет начальный размер данных 1 new_exports.set_name("new_exp"); //Ð˜Ð¼Ñ Ñекции new_exports.readable(true); //ДоÑтупна на чтение - pe_base::section& attached_section = image->add_section(new_exports); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами + section& attached_section = image.add_section(new_exports); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами - image->rebuild_exports(info, exports, attached_section); //ПереÑобираем ÑкÑпорты, раÑположив их Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° новой Ñекции и запиÑав новые данные таблиц ÑкÑпорта в PE-заголовок + rebuild_exports(image, info, exports, attached_section); //ПереÑобираем ÑкÑпорты, раÑположив их Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° новой Ñекции и запиÑав новые данные таблиц ÑкÑпорта в PE-заголовок //Создаем новый PE-файл std::string base_file_name(argv[1]); @@ -86,7 +86,7 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл - image->rebuild_pe(new_pe_file); + rebuild_pe(image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/exports_reader/exports_reader.vcproj b/samples/exports_reader/exports_reader.vcproj index 01ad968..d44a673 100644 --- a/samples/exports_reader/exports_reader.vcproj +++ b/samples/exports_reader/exports_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/exports_reader/exports_reader.vcxproj b/samples/exports_reader/exports_reader.vcxproj index fe61299..b81eaa0 100644 --- a/samples/exports_reader/exports_reader.vcxproj +++ b/samples/exports_reader/exports_reader.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> </ClCompile> diff --git a/samples/exports_reader/main.cpp b/samples/exports_reader/main.cpp index d41a90a..682c9f3 100644 --- a/samples/exports_reader/main.cpp +++ b/samples/exports_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,10 +27,10 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Проверим, еÑÑ‚ÑŒ ли ÑкÑпорты у PE-файла - if(!image->has_exports()) + if(!image.has_exports()) { std::cout << "Image has no exports" << std::endl; return 0; @@ -39,8 +39,8 @@ int main(int argc, char* argv[]) std::cout << "Reading PE exports..." << std::hex << std::showbase << std::endl << std::endl; //Получаем полную информацию об ÑкÑпортах и ÑпиÑок ÑкÑпортируемых функций - pe_base::export_info info; - const pe_base::exported_functions_list exports = image->get_exported_functions(info); + export_info info; + const exported_functions_list exports = get_exported_functions(image, info); //Выведем некоторую информацию об ÑкÑпорте: std::cout << "Export info" << std::endl @@ -50,9 +50,9 @@ int main(int argc, char* argv[]) << std::endl; //ПеречиÑлÑем Ñекции и выводим информацию о них - for(pe_base::exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) { - const pe_base::exported_function& func = *it; //ÐкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€ÑƒÐµÐ¼Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ + const exported_function& func = *it; //ÐкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€ÑƒÐµÐ¼Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ std::cout << "[+] "; if(func.has_name()) //ЕÑли Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¸Ð¼ÐµÐµÑ‚ имÑ, выведем его и ординал имени std::cout << func.get_name() << ", name ordinal: " << func.get_name_ordinal() << " "; diff --git a/samples/full_pe_rebuilder/full_pe_rebuilder.vcproj b/samples/full_pe_rebuilder/full_pe_rebuilder.vcproj index a3f7c1c..946e118 100644 --- a/samples/full_pe_rebuilder/full_pe_rebuilder.vcproj +++ b/samples/full_pe_rebuilder/full_pe_rebuilder.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/full_pe_rebuilder/full_pe_rebuilder.vcxproj b/samples/full_pe_rebuilder/full_pe_rebuilder.vcxproj index 11fceda..79d657d 100644 --- a/samples/full_pe_rebuilder/full_pe_rebuilder.vcxproj +++ b/samples/full_pe_rebuilder/full_pe_rebuilder.vcxproj @@ -95,7 +95,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> </ClCompile> @@ -131,7 +131,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/full_pe_rebuilder/main.cpp b/samples/full_pe_rebuilder/main.cpp index c9a1a3d..e7fcc76 100644 --- a/samples/full_pe_rebuilder/main.cpp +++ b/samples/full_pe_rebuilder/main.cpp @@ -1,8 +1,7 @@ #include <iostream> #include <fstream> #include <sstream> -#include <pe_factory.h> -#include <pe_32_64.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -14,7 +13,7 @@ int main(int argc, char* argv[]) { if(argc != 2) { - std::cout << "Usage: image_config_editor.exe PE_FILE" << std::endl; + std::cout << "Usage: full_pe_rebuilder.exe PE_FILE" << std::endl; return 0; } @@ -33,37 +32,37 @@ int main(int argc, char* argv[]) { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Создаем новый пуÑтой образ - new_image.reset(image->get_pe_type() == pe_base::pe_type_32 - ? static_cast<pe_base*>(new pe32(image->get_section_alignment())) - : static_cast<pe_base*>(new pe64(image->get_section_alignment()))); + new_image.reset(image.get_pe_type() == pe_type_32 + ? static_cast<pe_base*>(new pe_base(pe_properties_32(), image.get_section_alignment())) + : static_cast<pe_base*>(new pe_base(pe_properties_64(), image.get_section_alignment()))); //Копируем важные параметры Ñтарого образа в новый - new_image->set_characteristics(image->get_characteristics()); - new_image->set_dll_characteristics(image->get_dll_characteristics()); - new_image->set_file_alignment(image->get_file_alignment()); - new_image->set_heap_size_commit(image->get_heap_size_commit_64()); - new_image->set_heap_size_reserve(image->get_heap_size_reserve_64()); - new_image->set_stack_size_commit(image->get_stack_size_commit_64()); - new_image->set_stack_size_reserve(image->get_stack_size_reserve_64()); - new_image->set_image_base_64(image->get_image_base_64()); - new_image->set_ep(image->get_ep()); + new_image->set_characteristics(image.get_characteristics()); + new_image->set_dll_characteristics(image.get_dll_characteristics()); + new_image->set_file_alignment(image.get_file_alignment()); + new_image->set_heap_size_commit(image.get_heap_size_commit_64()); + new_image->set_heap_size_reserve(image.get_heap_size_reserve_64()); + new_image->set_stack_size_commit(image.get_stack_size_commit_64()); + new_image->set_stack_size_reserve(image.get_stack_size_reserve_64()); + new_image->set_image_base_64(image.get_image_base_64()); + new_image->set_ep(image.get_ep()); new_image->set_number_of_rvas_and_sizes(new_image->get_number_of_rvas_and_sizes()); - new_image->set_subsystem(image->get_subsystem()); + new_image->set_subsystem(image.get_subsystem()); //Копируем вÑе ÑущеÑтвующие директории - for(unsigned long i = 0; i < image->get_number_of_rvas_and_sizes(); ++i) + for(unsigned long i = 0; i < image.get_number_of_rvas_and_sizes(); ++i) { - new_image->set_directory_rva(i, image->get_directory_rva(i)); - new_image->set_directory_size(i, image->get_directory_size(i)); + new_image->set_directory_rva(i, image.get_directory_rva(i)); + new_image->set_directory_size(i, image.get_directory_size(i)); } //Копируем данные Ñекций { - const pe_base::section_list& pe_sections = image->get_image_sections(); - for(pe_base::section_list::const_iterator it = pe_sections.begin(); it != pe_sections.end(); ++it) + const section_list& pe_sections = image.get_image_sections(); + for(section_list::const_iterator it = pe_sections.begin(); it != pe_sections.end(); ++it) new_image->set_section_virtual_size(new_image->add_section(*it), (*it).get_virtual_size()); } } @@ -73,8 +72,8 @@ int main(int argc, char* argv[]) //и Ñохраним ее (Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÑ€Ð°) { std::stringstream temp_pe(std::ios::out | std::ios::in | std::ios::binary); - new_image->rebuild_pe(temp_pe); - new_image->set_checksum(pe_base::calculate_checksum(temp_pe)); + rebuild_pe(*new_image, temp_pe); + new_image->set_checksum(calculate_checksum(temp_pe)); } @@ -92,8 +91,10 @@ int main(int argc, char* argv[]) return -1; } + new_image->set_stub_overlay("alalalalala alalalala!!! alalalala alalalalalala!!!!!!"); + //ПереÑобираем PE-файл из нового обраща - new_image->rebuild_pe(new_pe_file); + rebuild_pe(*new_image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/image_config_editor/image_config_editor.vcproj b/samples/image_config_editor/image_config_editor.vcproj index 398fd64..154145e 100644 --- a/samples/image_config_editor/image_config_editor.vcproj +++ b/samples/image_config_editor/image_config_editor.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/image_config_editor/image_config_editor.vcxproj b/samples/image_config_editor/image_config_editor.vcxproj index fb83c0c..960563d 100644 --- a/samples/image_config_editor/image_config_editor.vcxproj +++ b/samples/image_config_editor/image_config_editor.vcxproj @@ -95,7 +95,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> </ClCompile> @@ -131,7 +131,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/image_config_editor/main.cpp b/samples/image_config_editor/main.cpp index b349817..baf79e0 100644 --- a/samples/image_config_editor/main.cpp +++ b/samples/image_config_editor/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,17 +27,17 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Получим информацию о директории Load Config - pe_base::image_config_info info = image->get_image_config(); + image_config_info info(get_image_config(image)); //Ðо переÑоберем Ñту директорию, раÑположив ее в новой Ñекции - pe_base::section load_config; + section load_config; load_config.get_raw_data().resize(1); //Мы не можем добавлÑÑ‚ÑŒ пуÑтые Ñекции, поÑтому пуÑÑ‚ÑŒ у нее будет начальный размер данных 1 load_config.set_name("load_cfg"); //Ð˜Ð¼Ñ Ñекции load_config.readable(true).writeable(true); //ДоÑтупна на чтение и запиÑÑŒ - pe_base::section& attached_section = image->add_section(load_config); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами + section& attached_section = image.add_section(load_config); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами //ЕÑли у файла была таблица SE Handler'ов if(info.get_se_handler_table_va()) @@ -50,7 +50,7 @@ int main(int argc, char* argv[]) //ПереÑобираем директорию Image Load Config, переÑобираем таблицу Lock-префикÑов, еÑли она имелаÑÑŒ, а также //таблицу SE Handler'ов, еÑли она еÑÑ‚ÑŒ - image->rebuild_image_config(info, attached_section, 1); + rebuild_image_config(image, info, attached_section, 1); //Создаем новый PE-файл std::string base_file_name(argv[1]); @@ -67,7 +67,7 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл - image->rebuild_pe(new_pe_file); + rebuild_pe(image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/import_adder/import_adder.vcproj b/samples/import_adder/import_adder.vcproj index 93aacc4..d51f9d4 100644 --- a/samples/import_adder/import_adder.vcproj +++ b/samples/import_adder/import_adder.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/import_adder/import_adder.vcxproj b/samples/import_adder/import_adder.vcxproj index 9db6489..314be62 100644 --- a/samples/import_adder/import_adder.vcxproj +++ b/samples/import_adder/import_adder.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/import_adder/main.cpp b/samples/import_adder/main.cpp index b5e9625..2f89dc2 100644 --- a/samples/import_adder/main.cpp +++ b/samples/import_adder/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,21 +27,21 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Получим ÑпиÑок импортируемых библиотек и функций - pe_base::imported_functions_list imports = image->get_imported_functions(); + imported_functions_list imports(get_imported_functions(image)); //Создадим новую библиотеку, из которой будем импортировать функции - pe_base::import_library new_lib; + import_library new_lib; new_lib.set_name("kaimi_dx.dll"); //ПуÑÑ‚ÑŒ Ñто будет testdll.dll //Добавим к ней пару импортов функций - pe_base::imported_function func; + imported_function func; func.set_name("Tralala"); //Один импорт - по имени Tralala func.set_iat_va(0xf1ac); //Запишем ненулевой абÑолютный Ð°Ð´Ñ€ÐµÑ import address table - pe_base::imported_function func2; + imported_function func2; func2.set_ordinal(5); //Другой импорт - по ординалу 5 func2.set_iat_va(0xf1be); //Запишем ненулевой абÑолютный Ð°Ð´Ñ€ÐµÑ import address table @@ -59,15 +59,15 @@ int main(int argc, char* argv[]) //Она будет иметь больший размер, чем до нашего редактированиÑ, //поÑтому запишем ее в новую Ñекцию, чтобы вÑе помеÑтилоÑÑŒ //(мы не можем раÑширÑÑ‚ÑŒ ÑущеÑтвующие Ñекции, еÑли только ÑÐµÐºÑ†Ð¸Ñ Ð½Ðµ в Ñамом конце файла) - pe_base::section new_imports; + section new_imports; new_imports.get_raw_data().resize(1); //Мы не можем добавлÑÑ‚ÑŒ пуÑтые Ñекции, поÑтому пуÑÑ‚ÑŒ у нее будет начальный размер данных 1 new_imports.set_name("new_imp"); //Ð˜Ð¼Ñ Ñекции new_imports.readable(true).writeable(true); //ДоÑтупна на чтение и запиÑÑŒ - pe_base::section& attached_section = image->add_section(new_imports); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами + section& attached_section = image.add_section(new_imports); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами //Структура, Ð¾Ñ‚Ð²ÐµÑ‡Ð°ÑŽÑ‰Ð°Ñ Ð·Ð° наÑтройки переÑборщика импортов - pe_base::import_rebuilder_settings settings(true, false); //Модифицируем заголовок PE и не очищаем поле IMAGE_DIRECTORY_ENTRY_IAT - image->rebuild_imports(imports, attached_section, settings); //ПереÑобираем импорты + import_rebuilder_settings settings(true, false); //Модифицируем заголовок PE и не очищаем поле IMAGE_DIRECTORY_ENTRY_IAT + rebuild_imports(image, imports, attached_section, settings); //ПереÑобираем импорты //Создаем новый PE-файл std::string base_file_name(argv[1]); @@ -84,7 +84,7 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл - image->rebuild_pe(new_pe_file); + rebuild_pe(image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/imports_reader/imports_reader.vcproj b/samples/imports_reader/imports_reader.vcproj index a097df7..07cd0c1 100644 --- a/samples/imports_reader/imports_reader.vcproj +++ b/samples/imports_reader/imports_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/imports_reader/imports_reader.vcxproj b/samples/imports_reader/imports_reader.vcxproj index b7a636a..7d6c9c7 100644 --- a/samples/imports_reader/imports_reader.vcxproj +++ b/samples/imports_reader/imports_reader.vcxproj @@ -104,7 +104,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <EnablePREfast>false</EnablePREfast> @@ -141,7 +141,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> </ClCompile> diff --git a/samples/imports_reader/main.cpp b/samples/imports_reader/main.cpp index 819ffd9..225b402 100644 --- a/samples/imports_reader/main.cpp +++ b/samples/imports_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,10 +27,10 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Проверим, еÑÑ‚ÑŒ ли импорты у файла - if(!image->has_imports()) + if(!image.has_imports()) { std::cout << "Image has no imports" << std::endl; return 0; @@ -39,22 +39,22 @@ int main(int argc, char* argv[]) std::cout << "Reading PE imports..." << std::hex << std::showbase << std::endl << std::endl; //Получаем ÑпиÑок импортируемых библиотек Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñми - const pe_base::imported_functions_list imports = image->get_imported_functions(); + const imported_functions_list imports = get_imported_functions(image); //ПеречиÑлÑем импортированные библиотеки и выводим информацию о них - for(pe_base::imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) { - const pe_base::import_library& lib = *it; //Ð˜Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¸Ñ€ÑƒÐµÐ¼Ð°Ñ Ð±Ð¸Ð±Ð»Ð¸Ð¾Ñ‚ÐµÐºÐ° + const import_library& lib = *it; //Ð˜Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¸Ñ€ÑƒÐµÐ¼Ð°Ñ Ð±Ð¸Ð±Ð»Ð¸Ð¾Ñ‚ÐµÐºÐ° std::cout << "Library [" << lib.get_name() << "]" << std::endl //Ð˜Ð¼Ñ << "Timestamp: " << lib.get_timestamp() << std::endl //Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð¼ÐµÑ‚ÐºÐ° << "RVA to IAT: " << lib.get_rva_to_iat() << std::endl //ОтноÑительный Ð°Ð´Ñ€ÐµÑ Ðº import address table << "========" << std::endl; //ПеречиÑлÑем импортированные функции Ð´Ð»Ñ Ð±Ð¸Ð±Ð»Ð¸Ð¾Ñ‚ÐµÐºÐ¸ - const pe_base::import_library::imported_list& functions = lib.get_imported_functions(); - for(pe_base::import_library::imported_list::const_iterator func_it = functions.begin(); func_it != functions.end(); ++func_it) + const import_library::imported_list& functions = lib.get_imported_functions(); + for(import_library::imported_list::const_iterator func_it = functions.begin(); func_it != functions.end(); ++func_it) { - const pe_base::imported_function& func = *func_it; //Ð˜Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ + const imported_function& func = *func_it; //Ð˜Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ std::cout << "[+] "; if(func.has_name()) //ЕÑли Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¸Ð¼ÐµÐµÑ‚ Ð¸Ð¼Ñ - выведем его std::cout << func.get_name(); diff --git a/samples/pe_config_reader/main.cpp b/samples/pe_config_reader/main.cpp index 2bddbb5..1a11ead 100644 --- a/samples/pe_config_reader/main.cpp +++ b/samples/pe_config_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,12 +27,12 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); std::cout << "Reading PE image config info..." << std::hex << std::showbase << std::endl << std::endl; //Получаем конфигурацию - const pe_base::image_config_info info = image->get_image_config(); + const image_config_info info(get_image_config(image)); //Выводим данные конфигурации //Подробнее о полÑÑ… - в MSDN @@ -52,8 +52,8 @@ int main(int argc, char* argv[]) << std::endl; //Выведем адреÑа SE-хендлеров - const pe_base::image_config_info::se_handler_list& se_handlers = info.get_se_handler_rvas(); - for(pe_base::image_config_info::se_handler_list::const_iterator it = se_handlers.begin(); it != se_handlers.end(); ++it) + const image_config_info::se_handler_list& se_handlers = info.get_se_handler_rvas(); + for(image_config_info::se_handler_list::const_iterator it = se_handlers.begin(); it != se_handlers.end(); ++it) std::cout << "SE Handler: " << (*it) << std::endl; } catch(const pe_exception& e) diff --git a/samples/pe_config_reader/pe_config_reader.vcproj b/samples/pe_config_reader/pe_config_reader.vcproj index e6ce7c3..8eaab96 100644 --- a/samples/pe_config_reader/pe_config_reader.vcproj +++ b/samples/pe_config_reader/pe_config_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/pe_config_reader/pe_config_reader.vcxproj b/samples/pe_config_reader/pe_config_reader.vcxproj index 84c9dad..ccd6357 100644 --- a/samples/pe_config_reader/pe_config_reader.vcxproj +++ b/samples/pe_config_reader/pe_config_reader.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/pe_realigner/main.cpp b/samples/pe_realigner/main.cpp index a899888..bc62126 100644 --- a/samples/pe_realigner/main.cpp +++ b/samples/pe_realigner/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,10 +27,10 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Выведем темущий file alignment - std::cout << "File alignment: " << image->get_file_alignment() << std::endl; + std::cout << "File alignment: " << image.get_file_alignment() << std::endl; //Предложим выбрать новое выравнивание unsigned int new_alignment_index = static_cast<unsigned int>(-1); @@ -52,7 +52,7 @@ int main(int argc, char* argv[]) unsigned int available_aligns[] = {512, 1024, 2048, 4096}; //Изменим выравнивание на то, которое указал пользователь - image->realign_file(available_aligns[new_alignment_index]); + image.realign_file(available_aligns[new_alignment_index]); //Создаем новый PE-файл std::string base_file_name(argv[1]); @@ -69,7 +69,7 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл - image->rebuild_pe(new_pe_file); + rebuild_pe(image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/pe_realigner/pe_realigner.vcproj b/samples/pe_realigner/pe_realigner.vcproj index 9baff78..91f3ce1 100644 --- a/samples/pe_realigner/pe_realigner.vcproj +++ b/samples/pe_realigner/pe_realigner.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/pe_realigner/pe_realigner.vcxproj b/samples/pe_realigner/pe_realigner.vcxproj index e8e9df3..7e175d4 100644 --- a/samples/pe_realigner/pe_realigner.vcxproj +++ b/samples/pe_realigner/pe_realigner.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/pe_rebaser/main.cpp b/samples/pe_rebaser/main.cpp index 1b6b9f3..e8950fc 100644 --- a/samples/pe_rebaser/main.cpp +++ b/samples/pe_rebaser/main.cpp @@ -1,7 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> -#include <stdint_defs.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -28,21 +27,21 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Проверим, еÑÑ‚ÑŒ ли релокации у образа - if(!image->has_reloc()) + if(!image.has_reloc()) { std::cout << "Image has no relocations, rebase is not possible" << std::endl; return 0; } //Получим значение базового адреÑа загрузки образа (64-бита, универÑально Ð´Ð»Ñ PE и PE+) - uint64_t base = image->get_image_base_64(); + uint64_t base = image.get_image_base_64(); base += 0x100000; //Изменим базовый Ð°Ð´Ñ€ÐµÑ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ - //Произведем переÑчет необходимых байтов - image->rebase_image(image->get_relocations(), base); + //Произведем переÑчет необходимых адреÑов + rebase_image(image, get_relocations(image), base); //Создаем новый PE-файл std::string base_file_name(argv[1]); @@ -59,7 +58,7 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл - image->rebuild_pe(new_pe_file); + rebuild_pe(image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/pe_rebaser/pe_rebaser.vcproj b/samples/pe_rebaser/pe_rebaser.vcproj index ad6e87c..e148b49 100644 --- a/samples/pe_rebaser/pe_rebaser.vcproj +++ b/samples/pe_rebaser/pe_rebaser.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/pe_rebaser/pe_rebaser.vcxproj b/samples/pe_rebaser/pe_rebaser.vcxproj index 2ae4f94..a93030b 100644 --- a/samples/pe_rebaser/pe_rebaser.vcxproj +++ b/samples/pe_rebaser/pe_rebaser.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/pe_sections_reader/main.cpp b/samples/pe_sections_reader/main.cpp index 9d89f36..ee33ea7 100644 --- a/samples/pe_sections_reader/main.cpp +++ b/samples/pe_sections_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,16 +27,16 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Получаем ÑпиÑок Ñекций std::cout << "Reading PE sections..." << std::hex << std::showbase << std::endl << std::endl; - const pe_base::section_list sections = image->get_image_sections(); + const section_list sections(image.get_image_sections()); //ПеречиÑлÑем Ñекции и выводим информацию о них - for(pe_base::section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) { - const pe_base::section& s = *it; //Ð¡ÐµÐºÑ†Ð¸Ñ + const section& s = *it; //Ð¡ÐµÐºÑ†Ð¸Ñ std::cout << "Section [" << s.get_name() << "]" << std::endl //Ð˜Ð¼Ñ Ñекции << "Characteristics: " << s.get_characteristics() << std::endl //ХарактериÑтики << "Size of raw data: " << s.get_size_of_raw_data() << std::endl //Размер данных в файле diff --git a/samples/pe_sections_reader/pe_sections_reader.vcproj b/samples/pe_sections_reader/pe_sections_reader.vcproj index 2a19539..75a1534 100644 --- a/samples/pe_sections_reader/pe_sections_reader.vcproj +++ b/samples/pe_sections_reader/pe_sections_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/pe_sections_reader/pe_sections_reader.vcxproj b/samples/pe_sections_reader/pe_sections_reader.vcxproj index c329183..d9b125d 100644 --- a/samples/pe_sections_reader/pe_sections_reader.vcxproj +++ b/samples/pe_sections_reader/pe_sections_reader.vcxproj @@ -105,7 +105,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -144,7 +144,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/pe_stripper/main.cpp b/samples/pe_stripper/main.cpp index a893fdc..a992038 100644 --- a/samples/pe_stripper/main.cpp +++ b/samples/pe_stripper/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,14 +27,14 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Удалим DOS stub и rich overlay - image->strip_stub_overlay(); + image.strip_stub_overlay(); //Удалим ненужные DATA_DIRECTORY (нулевые) //Очень малое количеÑтво линкеров умеют Ñто делать - image->strip_data_directories(0); + image.strip_data_directories(0); //Создаем новый PE-файл std::string base_file_name(argv[1]); @@ -51,10 +51,10 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл Ñ Ð¾Ð¿Ñ†Ð¸ÐµÐ¹ ÑÐ¶Ð°Ñ‚Ð¸Ñ DOS-header - //УÑÐµÐ½ÑŒÑˆÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° Ñто не дает, но упаковывает NT-заголовки в DOS-заголовок + //Ð£Ð¼ÐµÐ½ÑŒÑˆÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° Ñто не дает, но упаковывает NT-заголовки в DOS-заголовок //При переÑборке автоматичеÑки убираютÑÑ Ð½ÐµÐ½ÑƒÐ¶Ð½Ñ‹Ðµ нулевые байты в Ñамом конце образа, //в результате чего размер образа ÑтановитÑÑ Ð½ÐµÐ¼Ð½Ð¾Ð³Ð¾ меньше - image->rebuild_pe(new_pe_file, true); + rebuild_pe(image, new_pe_file, true); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/pe_stripper/pe_stripper.vcproj b/samples/pe_stripper/pe_stripper.vcproj index 99fba5f..4dea5a5 100644 --- a/samples/pe_stripper/pe_stripper.vcproj +++ b/samples/pe_stripper/pe_stripper.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/pe_stripper/pe_stripper.vcxproj b/samples/pe_stripper/pe_stripper.vcxproj index dcc0263..9a943cc 100644 --- a/samples/pe_stripper/pe_stripper.vcxproj +++ b/samples/pe_stripper/pe_stripper.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/relocation_adder/main.cpp b/samples/relocation_adder/main.cpp index 70d2ba0..0c49023 100644 --- a/samples/relocation_adder/main.cpp +++ b/samples/relocation_adder/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,19 +27,19 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //ПеречиÑлим и получим вÑе запиÑи из таблиц релокаций в PE-файле, кроме абÑолютных //Можно было бы включить в ÑпиÑок и абÑолютные запиÑи (ABSOLUTE), передав в вызов true //Ðти запиÑи не нужны при переÑборке релокаций, они иÑпользуютÑÑ Ð´Ð»Ñ Ð²Ñ‹Ñ€Ð°Ð²Ð½Ð¸Ð²Ð°Ð½Ð¸Ñ //и будут добавлены автоматичеÑки переÑборщиком - pe_base::relocation_table_list tables = image->get_relocations(); + relocation_table_list tables(get_relocations(image)); //Создаем новую таблицу релокаций - pe_base::relocation_table new_table; + relocation_table new_table; new_table.set_rva(0x5678); //ОтноÑительный Ð°Ð´Ñ€ÐµÑ Ñ€ÐµÐ»Ð¾ÐºÐ°Ñ†Ð¸Ð¹ в таблице - он некорректен, Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÑ€Ð°, поÑтому получившийÑÑ PE Ñкорее вÑего не загрузитÑÑ //Добавим в таблицу новую релокацию - new_table.add_relocation(pe_base::relocation_entry(10, 3)); //Тип 3 - HIGHLOW-релокациÑ, RRVA = 10, Ñ‚.е. RVA = 0x5678 + 10 + new_table.add_relocation(relocation_entry(10, 3)); //Тип 3 - HIGHLOW-релокациÑ, RRVA = 10, Ñ‚.е. RVA = 0x5678 + 10 //ДобавлÑем таблицу tables.push_back(new_table); @@ -49,13 +49,13 @@ int main(int argc, char* argv[]) //Они будет иметь больший размер, чем до нашего редактированиÑ, //поÑтому запишем их в новую Ñекцию, чтобы вÑе помеÑтилоÑÑŒ //(мы не можем раÑширÑÑ‚ÑŒ ÑущеÑтвующие Ñекции, еÑли только ÑÐµÐºÑ†Ð¸Ñ Ð½Ðµ в Ñамом конце файла) - pe_base::section new_relocs; + section new_relocs; new_relocs.get_raw_data().resize(1); //Мы не можем добавлÑÑ‚ÑŒ пуÑтые Ñекции, поÑтому пуÑÑ‚ÑŒ у нее будет начальный размер данных 1 new_relocs.set_name("new_rel"); //Ð˜Ð¼Ñ Ñекции new_relocs.readable(true); //ДоÑтупна на чтение - pe_base::section& attached_section = image->add_section(new_relocs); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами + section& attached_section = image.add_section(new_relocs); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами - image->rebuild_relocations(tables, attached_section); //ПереÑобираем ÑкÑпорты, раÑположив их Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° новой Ñекции и запиÑав новые данные таблиц релокаций в PE-заголовок + rebuild_relocations(image, tables, attached_section); //ПереÑобираем ÑкÑпорты, раÑположив их Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° новой Ñекции и запиÑав новые данные таблиц релокаций в PE-заголовок //Создаем новый PE-файл std::string base_file_name(argv[1]); @@ -72,7 +72,7 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл - image->rebuild_pe(new_pe_file); + rebuild_pe(image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/relocation_adder/relocation_adder.vcproj b/samples/relocation_adder/relocation_adder.vcproj index f87b987..cb15018 100644 --- a/samples/relocation_adder/relocation_adder.vcproj +++ b/samples/relocation_adder/relocation_adder.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/relocation_adder/relocation_adder.vcxproj b/samples/relocation_adder/relocation_adder.vcxproj index e4e1758..8dccd51 100644 --- a/samples/relocation_adder/relocation_adder.vcxproj +++ b/samples/relocation_adder/relocation_adder.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/relocations_reader/main.cpp b/samples/relocations_reader/main.cpp index d094959..f7f5d1a 100644 --- a/samples/relocations_reader/main.cpp +++ b/samples/relocations_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,10 +27,10 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Проверим, еÑÑ‚ÑŒ ли релокации у файла - if(!image->has_reloc()) + if(!image.has_reloc()) { std::cout << "Image has no relocations" << std::endl; return 0; @@ -39,19 +39,19 @@ int main(int argc, char* argv[]) std::cout << "Reading PE relocations..." << std::hex << std::showbase << std::endl << std::endl; //Получаем ÑпиÑок таблиц релокаций - const pe_base::relocation_table_list tables = image->get_relocations(); + const relocation_table_list tables(get_relocations(image)); //ПеречиÑлÑем таблицы релокаций и выводим информацию о них - for(pe_base::relocation_table_list::const_iterator it = tables.begin(); it != tables.end(); ++it) + for(relocation_table_list::const_iterator it = tables.begin(); it != tables.end(); ++it) { - const pe_base::relocation_table& table = *it; //Таблица релокаций + const relocation_table& table = *it; //Таблица релокаций std::cout << "RVA [" << table.get_rva() << "]" << std::endl //ОтноÑительный Ð°Ð´Ñ€ÐµÑ << "==========" << std::endl; //ПеречиÑлим вÑе релокации - const pe_base::relocation_table::relocation_list& relocs = table.get_relocations(); - for(pe_base::relocation_table::relocation_list::const_iterator reloc_it = relocs.begin(); reloc_it != relocs.end(); ++reloc_it) + const relocation_table::relocation_list& relocs = table.get_relocations(); + for(relocation_table::relocation_list::const_iterator reloc_it = relocs.begin(); reloc_it != relocs.end(); ++reloc_it) { std::cout << "[+] " << (*reloc_it).get_item() << std::endl; } diff --git a/samples/relocations_reader/relocations_reader.vcproj b/samples/relocations_reader/relocations_reader.vcproj index 1ac4a11..9b09304 100644 --- a/samples/relocations_reader/relocations_reader.vcproj +++ b/samples/relocations_reader/relocations_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/relocations_reader/relocations_reader.vcxproj b/samples/relocations_reader/relocations_reader.vcxproj index 7c89f9d..d22e666 100644 --- a/samples/relocations_reader/relocations_reader.vcxproj +++ b/samples/relocations_reader/relocations_reader.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> </ClCompile> diff --git a/samples/resource_editor/main.cpp b/samples/resource_editor/main.cpp index a83df57..ab219a6 100644 --- a/samples/resource_editor/main.cpp +++ b/samples/resource_editor/main.cpp @@ -1,7 +1,7 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> -#include <pe_resource_manager.h> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> #ifdef PE_BLISS_WINDOWS #include "resource.h" #include "lib.h" @@ -38,7 +38,7 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Суть примера будет ÑоÑтоÑÑ‚ÑŒ в Ñледующем: //Ð’ Ñам пример вкомпиливаетÑÑ Ð¸ÐºÐ¾Ð½ÐºÐ° в директорию Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ CUSTOM @@ -48,7 +48,7 @@ int main(int argc, char* argv[]) //Ðаконец, добавить какую-нибудь информацию о верÑии к файлу //Проверим, еÑÑ‚ÑŒ ли реÑурÑÑ‹ у файла - if(!image->has_resources()) + if(!image.has_resources()) { std::cout << "Image has no resources" << std::endl; return 0; @@ -56,7 +56,7 @@ int main(int argc, char* argv[]) //Получаем корневую директорию реÑурÑов std::cout << "Reading PE resources..." << std::hex << std::showbase << std::endl << std::endl; - pe_base::resource_directory root = image->get_resources(); + resource_directory root(get_resources(image)); //Ð”Ð»Ñ Ð¾Ð±Ð»ÐµÐ³Ñ‡ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñми и запиÑÑми реÑурÑов Ñозданы вÑпомогательные клаÑÑÑ‹ //Ðтот клаÑÑ Ð¿Ð¾Ð·Ð²Ð¾Ð»Ñет извлекать из PE-файлов любые реÑурÑÑ‹ и перезапиÑывать их @@ -74,33 +74,33 @@ int main(int argc, char* argv[]) //Получим нашу иконку из Ñтой директории: мы знаем, что ее ID=100 и она одна в директории имен, поÑтому делаем так //Получаем ее по нулевому индекÑу (можно было получить по Ñзыку, но Ñто незачем, Ñ‚.к. она единÑтвеннаÑ) - const pe_resource_viewer::resource_data_info data = res.get_resource_data_by_id(L"CUSTOM", IDR_CUSTOM1); + const resource_data_info data = res.get_resource_data_by_id(L"CUSTOM", IDR_CUSTOM1); //Ðеобходимо теперь добавить ее как главную иконку //Иконка Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ - Ñто иконка из той группы иконок, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ñледует Ñамой первой в ÑпиÑке групп иконок //Помните, что Ñначала идут именованные реÑурÑÑ‹, а потом реÑурÑÑ‹ Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð°Ð¼Ð¸, и вÑÑ‘ ÑортируетÑÑ //Создадим группу иконок Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ MAIN_ICON - res.add_icon(data.get_data(), //Данные файла иконки + resource_cursor_icon_writer(res).add_icon(data.get_data(), //Данные файла иконки L"MAIN_ICON", //Ð˜Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ иконок (помните, у Ð½Ð°Ñ Ñ‚Ñ€Ð¸ картинки внутри иконки, они будут находитьÑÑ Ð² Ñтой группе) 0, //Язык - нам неважен - pe_resource_manager::icon_place_after_max_icon_id, //Вариант раÑÐ¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸ÐºÐ¾Ð½Ð¾Ðº в ÑущеÑтвующей группе - нам он неважен, так как мы Ñоздаем новую группу + resource_cursor_icon_writer::icon_place_after_max_icon_id, //Вариант раÑÐ¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸ÐºÐ¾Ð½Ð¾Ðº в ÑущеÑтвующей группе - нам он неважен, так как мы Ñоздаем новую группу data.get_codepage(), //Сохраним иÑходную Codepage 0 //Timestamp - неважен ); - //Теперь удалим уже ненужную директорию CUSTOM + //Теперь удалим уже ненужный реÑÑƒÑ€Ñ CUSTOM res.remove_resource(L"CUSTOM"); //Теперь Ñоздадим информацию о верÑии - pe_resource_viewer::file_version_info file_info; //Ð‘Ð°Ð·Ð¾Ð²Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ файле + file_version_info file_info; //Ð‘Ð°Ð·Ð¾Ð²Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ файле file_info.set_special_build(true); //Ðто будет Ñпециальный билд - file_info.set_file_os(pe_resource_viewer::file_version_info::file_os_nt_win32); //СиÑтема, на которой работает файл + file_info.set_file_os(file_version_info::file_os_nt_win32); //СиÑтема, на которой работает файл file_info.set_file_version_ms(0x00010002); //ВерÑÐ¸Ñ Ñ„Ð°Ð¹Ð»Ð° будет 1.2.3.4 file_info.set_file_version_ls(0x00030004); //Теперь Ñоздадим Ñтроки Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸ÐµÐ¹ и транÑлÑции (переводы) - pe_resource_viewer::lang_string_values_map strings; - pe_resource_viewer::translation_values_map translations; + lang_string_values_map strings; + translation_values_map translations; //Ð”Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñо Ñтроками и транÑлÑциÑми еÑÑ‚ÑŒ вÑпомогательный клаÑÑ version_info_editor version(strings, translations); @@ -125,25 +125,25 @@ int main(int argc, char* argv[]) version.set_property(L"MyLittleProperty", L"Secret Value"); //УÑтановим информацию о верÑии - res.set_version_info(file_info, strings, translations, 1033); //1033 - руÑÑкий Ñзык + resource_version_info_writer(res).set_version_info(file_info, strings, translations, 1033); //1033 - руÑÑкий Ñзык //ОÑталоÑÑŒ переименовать Ñтарую Ñекцию реÑурÑов //Она называетÑÑ .rsrc //Переименование необходимо Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы Windows Explorer Ñмог Ñчитать из новой Ñекции иконку - image->section_from_directory(pe_win::image_directory_entry_resource).set_name("oldres"); + image.section_from_directory(pe_win::image_directory_entry_resource).set_name("oldres"); //ПереÑоберем реÑурÑÑ‹ //Они будет иметь больший размер, чем до нашего редактированиÑ, //поÑтому запишем их в новую Ñекцию, чтобы вÑе помеÑтилоÑÑŒ //(мы не можем раÑширÑÑ‚ÑŒ ÑущеÑтвующие Ñекции, еÑли только ÑÐµÐºÑ†Ð¸Ñ Ð½Ðµ в Ñамом конце файла) - pe_base::section new_resources; + section new_resources; new_resources.get_raw_data().resize(1); //Мы не можем добавлÑÑ‚ÑŒ пуÑтые Ñекции, поÑтому пуÑÑ‚ÑŒ у нее будет начальный размер данных 1 new_resources.set_name(".rsrc"); //Ð˜Ð¼Ñ Ñекции new_resources.readable(true); //ДоÑтупна на чтение - pe_base::section& attached_section = image->add_section(new_resources); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами + section& attached_section = image.add_section(new_resources); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами //Теперь переÑоберем реÑурÑÑ‹, раÑположив их в Ñамом начале новой Ñекции и поправив PE-заголовок, запиÑав туда новые параметры директории реÑурÑÑ‹ - image->rebuild_resources(root, attached_section); + rebuild_resources(image, root, attached_section); //Создаем новый PE-файл std::string base_file_name(pe_filename); @@ -160,7 +160,7 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл - image->rebuild_pe(new_pe_file); + rebuild_pe(image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/resource_editor/resource_editor.vcproj b/samples/resource_editor/resource_editor.vcproj index 10faae5..5a08170 100644 --- a/samples/resource_editor/resource_editor.vcproj +++ b/samples/resource_editor/resource_editor.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/resource_editor/resource_editor.vcxproj b/samples/resource_editor/resource_editor.vcxproj index bae7ac0..2257196 100644 --- a/samples/resource_editor/resource_editor.vcxproj +++ b/samples/resource_editor/resource_editor.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/resource_viewer/main.cpp b/samples/resource_viewer/main.cpp index 79a67fd..521069a 100644 --- a/samples/resource_viewer/main.cpp +++ b/samples/resource_viewer/main.cpp @@ -1,7 +1,7 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> -#include <pe_resource_manager.h> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -28,10 +28,10 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Проверим, еÑÑ‚ÑŒ ли реÑурÑÑ‹ у файла - if(!image->has_resources()) + if(!image.has_resources()) { std::cout << "Image has no resources" << std::endl; return 0; @@ -39,7 +39,7 @@ int main(int argc, char* argv[]) //Получаем корневую директорию реÑурÑов std::cout << "Reading PE resources..." << std::hex << std::showbase << std::endl << std::endl; - const pe_base::resource_directory root = image->get_resources(); + const resource_directory root(get_resources(image)); //Ð”Ð»Ñ Ð¾Ð±Ð»ÐµÐ³Ñ‡ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñми и запиÑÑми реÑурÑов Ñозданы вÑпомогательные клаÑÑÑ‹ //Ðтот клаÑÑ Ð¿Ð¾Ð·Ð²Ð¾Ð»Ñет извлекать из PE-файлов любые реÑурÑÑ‹ @@ -57,10 +57,10 @@ int main(int argc, char* argv[]) //Выведем иофнрмацию о верÑии, еÑли она ÑущеÑтвует if(res.resource_exists(pe_resource_viewer::resource_version)) { - pe_resource_viewer::lang_string_values_map strings; - pe_resource_viewer::translation_values_map translations; + lang_string_values_map strings; + translation_values_map translations; //Получаем ÑпиÑок Ñтрок, переводов и базовую информацию о файле - pe_resource_viewer::file_version_info file_info(res.get_version_info(strings, translations)); + file_version_info file_info(resource_version_info_reader(res).get_version_info(strings, translations)); //Выводить информацию будем в юникодный поток std::wstringstream version_info; @@ -71,20 +71,20 @@ int main(int argc, char* argv[]) version_info << std::endl; //Выведем Ñтроки Ð´Ð»Ñ Ñ€Ð°Ð·Ð½Ñ‹Ñ… транÑлÑций: - for(pe_resource_viewer::lang_string_values_map::const_iterator it = strings.begin(); it != strings.end(); ++it) + for(lang_string_values_map::const_iterator it = strings.begin(); it != strings.end(); ++it) { version_info << L"Translation ID: " << (*it).first << std::endl; //ПеречиÑлим запиÑи в таблице Ñтрок Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ транÑлÑции (перевода) - const pe_resource_viewer::string_values_map& string_table = (*it).second; - for(pe_resource_viewer::string_values_map::const_iterator str_it = string_table.begin(); str_it != string_table.end(); ++str_it) + const string_values_map& string_table = (*it).second; + for(string_values_map::const_iterator str_it = string_table.begin(); str_it != string_table.end(); ++str_it) version_info << (*str_it).first << L": " << (*str_it).second << std::endl; version_info << std::endl; } //Выведем доÑтупные переводы (транÑлÑции): - for(pe_resource_viewer::translation_values_map::const_iterator it = translations.begin(); it != translations.end(); ++it) + for(translation_values_map::const_iterator it = translations.begin(); it != translations.end(); ++it) version_info << L"Translation: language: " << (*it).first << ", codepage: " << (*it).second << std::endl; { @@ -140,14 +140,14 @@ int main(int argc, char* argv[]) //Получим Ñамую первую иконку Ð´Ð»Ñ Ñамого первого Ñзыка (по индекÑу 0) //ЕÑли надо было бы перечиÑлить Ñзыки Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ð¾Ð¹ иконки, можно было вызвать list_resource_languages //ЕÑли надо было бы получить иконку Ð´Ð»Ñ ÐºÐ¾Ð½ÐºÑ€ÐµÑ‚Ð½Ð¾Ð³Ð¾ Ñзыка, можно было вызвать get_icon_by_name (перегрузка Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸ÐµÐ¼ Ñзыка) - main_icon = res.get_icon_by_name(icon_name_list[0]); + main_icon = resource_cursor_icon_reader(res).get_icon_by_name(icon_name_list[0]); } else if(!icon_id_list.empty()) //ЕÑли нет именованных групп иконок, но еÑÑ‚ÑŒ группы Ñ ID { //Получим Ñамую первую иконку Ð´Ð»Ñ Ñамого первого Ñзыка (по индекÑу 0) //ЕÑли надо было бы перечиÑлить Ñзыки Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ð¾Ð¹ иконки, можно было вызвать list_resource_languages //ЕÑли надо было бы получить иконку Ð´Ð»Ñ ÐºÐ¾Ð½ÐºÑ€ÐµÑ‚Ð½Ð¾Ð³Ð¾ Ñзыка, можно было вызвать get_icon_by_id_lang - main_icon = res.get_icon_by_id(icon_id_list[0]); + main_icon = resource_cursor_icon_reader(res).get_icon_by_id(icon_id_list[0]); } //ЕÑли еÑÑ‚ÑŒ иконка... @@ -189,10 +189,10 @@ int main(int argc, char* argv[]) { string_data << L" -> Language = " << *lang_it << std::endl; //Запишем Ñзык //Таблица Ñтрок - pe_resource_viewer::string_list strings(res.get_string_table_by_id_lang(*lang_it, *it)); + resource_string_list strings(resource_string_table_reader(res).get_string_table_by_id_lang(*lang_it, *it)); //Ðаконец, запишем вÑе Ñтроки в поток - for(pe_resource_viewer::string_list::const_iterator str_it = strings.begin(); str_it != strings.end(); ++str_it) + for(resource_string_list::const_iterator str_it = strings.begin(); str_it != strings.end(); ++str_it) string_data << L" --> #" << (*str_it).first << L": " << (*str_it).second << std::endl; //ID Ñтроки: ее значение } diff --git a/samples/resource_viewer/resource_viewer.vcproj b/samples/resource_viewer/resource_viewer.vcproj index b03b202..1a2b4ff 100644 --- a/samples/resource_viewer/resource_viewer.vcproj +++ b/samples/resource_viewer/resource_viewer.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/resource_viewer/resource_viewer.vcxproj b/samples/resource_viewer/resource_viewer.vcxproj index ca0a5ad..acb296c 100644 --- a/samples/resource_viewer/resource_viewer.vcxproj +++ b/samples/resource_viewer/resource_viewer.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/rich_overlay_stub_reader/main.cpp b/samples/rich_overlay_stub_reader/main.cpp index 05eb0f3..105aa8f 100644 --- a/samples/rich_overlay_stub_reader/main.cpp +++ b/samples/rich_overlay_stub_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,14 +27,14 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Выведем длину DOS stub'а - std::cout << "Image stub length: " << image->get_stub_overlay().length() << std::endl << std::endl; + std::cout << "Image stub length: " << image.get_stub_overlay().length() << std::endl << std::endl; //ПеречиÑлÑем вÑе RICH-запиÑи - pe_base::rich_data_list data = image->get_rich_data(); - for(pe_base::rich_data_list::const_iterator it = data.begin(); it != data.end(); ++it) + rich_data_list data = get_rich_data(image); + for(rich_data_list::const_iterator it = data.begin(); it != data.end(); ++it) { //Выводим информацию о запиÑи std::cout << "Number: " << (*it).get_number() << std::endl @@ -44,7 +44,7 @@ int main(int argc, char* argv[]) } //Отобразим информацию о том, еÑÑ‚ÑŒ ли у файла оверлей в конце (у некоторых инÑталлÑторов, например, еÑÑ‚ÑŒ) - std::cout << "Has overlay in the end: " << (image->has_overlay() ? "YES" : "NO") << std::endl; + std::cout << "Has overlay in the end: " << (image.has_overlay() ? "YES" : "NO") << std::endl; } catch(const pe_exception& e) { diff --git a/samples/rich_overlay_stub_reader/rich_overlay_stub_reader.vcproj b/samples/rich_overlay_stub_reader/rich_overlay_stub_reader.vcproj index 460e89d..d825d3a 100644 --- a/samples/rich_overlay_stub_reader/rich_overlay_stub_reader.vcproj +++ b/samples/rich_overlay_stub_reader/rich_overlay_stub_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/rich_overlay_stub_reader/rich_overlay_stub_reader.vcxproj b/samples/rich_overlay_stub_reader/rich_overlay_stub_reader.vcxproj index 3122066..56f67c5 100644 --- a/samples/rich_overlay_stub_reader/rich_overlay_stub_reader.vcxproj +++ b/samples/rich_overlay_stub_reader/rich_overlay_stub_reader.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> </ClCompile> diff --git a/samples/section_adder/main.cpp b/samples/section_adder/main.cpp index c6a45a4..95efbc1 100644 --- a/samples/section_adder/main.cpp +++ b/samples/section_adder/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,11 +27,11 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Секцию можно добавить только поÑле вÑех ÑущеÑтвующих, чтобы PE-файл не иÑпортилÑÑ //Создаем новую Ñекцию - pe_base::section new_section; + section new_section; new_section.readable(true).writeable(true); //Делаем Ñекцию доÑтупной Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¸ запиÑи new_section.set_name("kaimi.ru"); //Ставим Ð¸Ð¼Ñ Ñекции - макÑимум 8 Ñимволов new_section.set_raw_data("Tralala"); //УÑтанавливаем данные Ñекции @@ -39,10 +39,10 @@ int main(int argc, char* argv[]) //ДобавлÑем Ñекцию. Ð’Ñе адреÑа переÑчитаютÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки //Вызов вернет ÑÑылку на уже добавленную Ñекцию Ñ Ð¿ÐµÑ€ÐµÑчитанными адреÑами //СовÑем пуÑтую Ñекцию к образу добавить нельзÑ, у нее должен быть ненулевой размер данных или виртуальный размер - pe_base::section& added_section = image->add_section(new_section); + section& added_section = image.add_section(new_section); //ЕÑли нужно изменить виртуальный размер Ñекции, то делаетÑÑ Ñто так: - image->set_section_virtual_size(added_section, 0x1000); + image.set_section_virtual_size(added_section, 0x1000); //Создаем новый PE-файл std::string base_file_name(argv[1]); @@ -59,7 +59,7 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл - image->rebuild_pe(new_pe_file); + rebuild_pe(image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/section_adder/section_adder.vcproj b/samples/section_adder/section_adder.vcproj index 0c51b2f..0a7b9ec 100644 --- a/samples/section_adder/section_adder.vcproj +++ b/samples/section_adder/section_adder.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/section_adder/section_adder.vcxproj b/samples/section_adder/section_adder.vcxproj index 58697f8..a923c54 100644 --- a/samples/section_adder/section_adder.vcxproj +++ b/samples/section_adder/section_adder.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/sections_and_addresses/main.cpp b/samples/sections_and_addresses/main.cpp index dccf36f..34f141e 100644 --- a/samples/sections_and_addresses/main.cpp +++ b/samples/sections_and_addresses/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,17 +27,17 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Выведем Ð¸Ð¼Ñ Ñекции, в которой находитÑÑ Ñ‚Ð¾Ñ‡ÐºÐ° входа PE-файла //Ð’ хитрых PE-файлах точка входа может находитьÑÑ Ð² заголовке, тогда section_from_rva броÑит иÑключение - std::cout << "EP section name: " << image->section_from_rva(image->get_ep()).get_name() << std::endl; + std::cout << "EP section name: " << image.section_from_rva(image.get_ep()).get_name() << std::endl; //Длина "Ñырых" (raw) данных Ñекции - std::cout << "EP section data length: " << image->section_data_length_from_rva(image->get_ep()) << std::endl; + std::cout << "EP section data length: " << image.section_data_length_from_rva(image.get_ep()) << std::endl; //ЕÑли у PE-файла еÑÑ‚ÑŒ импорты, выведем Ð¸Ð¼Ñ Ñекции, в которой они находÑÑ‚ÑÑ - if(image->has_imports()) - std::cout << "Import section name: " << image->section_from_directory(pe_win::image_directory_entry_import).get_name() << std::endl; + if(image.has_imports()) + std::cout << "Import section name: " << image.section_from_directory(pe_win::image_directory_entry_import).get_name() << std::endl; } catch(const pe_exception& e) { diff --git a/samples/sections_and_addresses/sections_and_addresses.vcproj b/samples/sections_and_addresses/sections_and_addresses.vcproj index b7f567c..d538778 100644 --- a/samples/sections_and_addresses/sections_and_addresses.vcproj +++ b/samples/sections_and_addresses/sections_and_addresses.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/sections_and_addresses/sections_and_addresses.vcxproj b/samples/sections_and_addresses/sections_and_addresses.vcxproj index 9819d6f..c30474a 100644 --- a/samples/sections_and_addresses/sections_and_addresses.vcxproj +++ b/samples/sections_and_addresses/sections_and_addresses.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> </ClCompile> diff --git a/samples/tls_editor/main.cpp b/samples/tls_editor/main.cpp index a525793..358cc1f 100644 --- a/samples/tls_editor/main.cpp +++ b/samples/tls_editor/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -27,27 +27,27 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); //Получим информацию о TLS PE-файла //ЕÑли TLS нет, Ñтот вызов выброÑит иÑключение - pe_base::tls_info info = image->get_tls_info(); + tls_info info(get_tls_info(image)); //ПереÑоберем TLS //Он, вероÑтно, будет иметь больший размер, чем до нашего редактированиÑ, //поÑтому запишем его в новую Ñекцию, чтобы вÑе помеÑтилоÑÑŒ //(мы не можем раÑширÑÑ‚ÑŒ ÑущеÑтвующие Ñекции, еÑли только ÑÐµÐºÑ†Ð¸Ñ Ð½Ðµ в Ñамом конце файла) - pe_base::section new_tls; + section new_tls; new_tls.get_raw_data().resize(1); //Мы не можем добавлÑÑ‚ÑŒ пуÑтые Ñекции, поÑтому пуÑÑ‚ÑŒ у нее будет начальный размер данных 1 new_tls.set_name("new_tls"); //Ð˜Ð¼Ñ Ñекции new_tls.readable(true); //ДоÑтупна на чтение - pe_base::section& attached_section = image->add_section(new_tls); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами + section& attached_section = image.add_section(new_tls); //Добавим Ñекцию и получим ÑÑылку на добавленную Ñекцию Ñ Ð¿Ñ€Ð¾Ñчитанными размерами if(info.get_callbacks_rva() != 0) //ЕÑли у TLS еÑÑ‚ÑŒ Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один коллбек info.add_tls_callback(0x100); //Добавим новый коллбек в TLS - отноÑительный адреÑ, Ñкорее вÑего, некорректен, поÑтому программа не запуÑтитÑÑ (проÑто Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÑ€Ð°) info.set_raw_data("Hello, world!"); //УÑтановим или заменим "Ñырые" данные TLS - info.set_raw_data_start_rva(image->rva_from_section_offset(attached_section, 0)); //РаÑположим их Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° добавленной Ñекции + info.set_raw_data_start_rva(image.rva_from_section_offset(attached_section, 0)); //РаÑположим их Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° добавленной Ñекции info.recalc_raw_data_end_rva(); //ПроÑчитаем новый конечный Ð°Ð´Ñ€ÐµÑ "Ñырых" данных //ПереÑобираем TLS, раÑположив их Ñ 50-го байта (будет выровнено, ÑÐµÐºÑ†Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ автоматичеÑки раÑширена) новой Ñекции и запиÑав новые данные TLS в PE-заголовок @@ -56,7 +56,7 @@ int main(int argc, char* argv[]) //tls_data_expand_raw позволÑет увеличить "Ñырой" размер Ñекции, то еÑÑ‚ÑŒ размер в файле //tls_data_expand_virtual позволÑет увеличить виртуальный размер Ñекции Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸ TLS //ЕÑли не хватит меÑта под данные TLS, будет запиÑана только их чаÑÑ‚ÑŒ, или вообще ничего запиÑано не будет - image->rebuild_tls(info, attached_section, 50); + rebuild_tls(image, info, attached_section, 50); //Создаем новый PE-файл std::string base_file_name(argv[1]); @@ -73,7 +73,7 @@ int main(int argc, char* argv[]) } //ПереÑобираем PE-файл - image->rebuild_pe(new_pe_file); + rebuild_pe(image, new_pe_file); std::cout << "PE was rebuilt and saved to " << base_file_name << std::endl; } diff --git a/samples/tls_editor/tls_editor.vcproj b/samples/tls_editor/tls_editor.vcproj index 7a8c0be..8c45205 100644 --- a/samples/tls_editor/tls_editor.vcproj +++ b/samples/tls_editor/tls_editor.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/tls_editor/tls_editor.vcxproj b/samples/tls_editor/tls_editor.vcxproj index 52993f2..70608fe 100644 --- a/samples/tls_editor/tls_editor.vcxproj +++ b/samples/tls_editor/tls_editor.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/samples/tls_reader/main.cpp b/samples/tls_reader/main.cpp index 6304cc6..847b827 100644 --- a/samples/tls_reader/main.cpp +++ b/samples/tls_reader/main.cpp @@ -1,6 +1,6 @@ #include <iostream> #include <fstream> -#include <pe_factory.h> +#include <pe_bliss.h> #ifdef PE_BLISS_WINDOWS #include "lib.h" #endif @@ -28,12 +28,12 @@ int main(int argc, char* argv[]) try { //Создаем ÑкземплÑÑ€ PE или PE+ клаÑÑа Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ фабрики - std::auto_ptr<pe_base> image = pe_factory::create_pe(pe_file); + pe_base image(pe_factory::create_pe(pe_file)); std::cout << "Reading PE TLS info..." << std::hex << std::showbase << std::endl << std::endl; //Получаем информацию о TLS - const pe_base::tls_info info = image->get_tls_info(); + const tls_info info = get_tls_info(image); //Выводим информацию о TLS std::cout << "Callbacks RVA: " << info.get_callbacks_rva() << std::endl @@ -43,8 +43,8 @@ int main(int argc, char* argv[]) << "Size of zero fill: " << info.get_size_of_zero_fill() << std::endl; //Выведем TLS-коллбеки: - const pe_base::tls_info::tls_callback_list& tls_callbacks = info.get_tls_callbacks(); - for(pe_base::tls_info::tls_callback_list::const_iterator it = tls_callbacks.begin(); it != tls_callbacks.end(); ++it) + const tls_info::tls_callback_list& tls_callbacks = info.get_tls_callbacks(); + for(tls_info::tls_callback_list::const_iterator it = tls_callbacks.begin(); it != tls_callbacks.end(); ++it) std::cout << "Callback: " << (*it) << std::endl; } catch(const pe_exception& e) diff --git a/samples/tls_reader/tls_reader.vcproj b/samples/tls_reader/tls_reader.vcproj index 1fcfdc7..a5c285e 100644 --- a/samples/tls_reader/tls_reader.vcproj +++ b/samples/tls_reader/tls_reader.vcproj @@ -92,12 +92,11 @@ /> </Configuration> <Configuration - Name="Release|Win32" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(ConfigurationName)" + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" - WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -113,15 +112,16 @@ /> <Tool Name="VCMIDLTool" + TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="2" - EnableIntrinsicFunctions="true" + Optimization="0" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" - RuntimeLibrary="0" - EnableFunctionLevelLinking="true" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -137,12 +137,10 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="1" + LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - TargetMachine="1" + TargetMachine="17" /> <Tool Name="VCALinkTool" @@ -167,11 +165,12 @@ /> </Configuration> <Configuration - Name="Debug|x64" - OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" - IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" + WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" @@ -187,16 +186,15 @@ /> <Tool Name="VCMIDLTool" - TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" - Optimization="0" + Optimization="2" + EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" - MinimalRebuild="true" - BasicRuntimeChecks="3" - RuntimeLibrary="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" @@ -212,10 +210,12 @@ /> <Tool Name="VCLinkerTool" - LinkIncremental="2" + LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" - TargetMachine="17" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" /> <Tool Name="VCALinkTool" @@ -268,7 +268,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../../pe_lib/;../" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/samples/tls_reader/tls_reader.vcxproj b/samples/tls_reader/tls_reader.vcxproj index 57e857d..ae665fe 100644 --- a/samples/tls_reader/tls_reader.vcxproj +++ b/samples/tls_reader/tls_reader.vcxproj @@ -98,7 +98,7 @@ </PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> <EnablePREfast>false</EnablePREfast> @@ -135,7 +135,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> </ClCompile> diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..ab12c16 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,21 @@ +TESTS = tests_utils tests_basic test_rich_data test_entropy test_runner test_checksum test_tls test_relocations test_load_config test_exception_directory test_imports test_exports test_resources test_bound_import test_resource_viewer test_dotnet test_debug test_resource_manager test_resource_bitmap test_resource_icon_cursor test_resource_string_table test_resource_message_table test_resource_version_info +OUTDIR = ./bin/ +LIBPATH = ../lib/libpebliss.a +TARGETS = $(foreach test,$(TESTS),$(test)_test) +TARGETS_CLEAN = $(foreach test,$(TESTS),$(test)_clean) + +all: $(TARGETS) run_all_tests + +clean: $(TARGETS_CLEAN) + +$(OUTDIR): + mkdir -p $(OUTDIR) + +%_test: $(OUTDIR) $(LIBPATH) + $(MAKE) PE_DEBUG=$(PE_DEBUG) -C ./$* + +%_clean: + $(MAKE) -C ./$* clean + +run_all_tests: + ./bin/test_runner diff --git a/tests/lib.h b/tests/lib.h new file mode 100644 index 0000000..bd981f1 --- /dev/null +++ b/tests/lib.h @@ -0,0 +1,14 @@ +#pragma once +#ifndef _M_X64 +#ifdef _DEBUG +#pragma comment(lib, "../../Debug/pe_bliss.lib") +#else +#pragma comment(lib, "../../Release/pe_bliss.lib") +#endif +#else +#ifdef _DEBUG +#pragma comment(lib, "../../x64/Debug/pe_bliss.lib") +#else +#pragma comment(lib, "../../x64/Release/pe_bliss.lib") +#endif +#endif diff --git a/tests/pe_files/TestApp.exe b/tests/pe_files/TestApp.exe new file mode 100644 index 0000000000000000000000000000000000000000..c9487ab657b050ec13d66137ebb4894f7ca74b17 GIT binary patch literal 4608 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 literal 0 HcmV?d00001 diff --git a/tests/pe_files/bound32.exe b/tests/pe_files/bound32.exe new file mode 100644 index 0000000000000000000000000000000000000000..1d593d3e0384afd81e78c64c24eaf5ba13f0eec6 GIT binary patch literal 243712 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�TCwj-vB#607L<#<zj2c>I*JV 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%0DlIX~A<f$@cs}K<DP(`qHa%H`Yvt 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 Z1^%MIUljO@0)J89FADrcfj^7_{|`#A=RyDg literal 0 HcmV?d00001 diff --git a/tests/pe_files/bound64.exe b/tests/pe_files/bound64.exe new file mode 100644 index 0000000000000000000000000000000000000000..e288befc9b9f38bf8854a8c4ad36c5958700139f GIT binary patch literal 265728 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 literal 0 HcmV?d00001 diff --git a/tests/pe_files/debug_test.exe b/tests/pe_files/debug_test.exe new file mode 100644 index 0000000000000000000000000000000000000000..0dd38a057d04a80045bd0c89041b9dbbfb281d0b GIT binary patch literal 114220 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 M7OrQdV;$^&0y7ywEdT%j literal 0 HcmV?d00001 diff --git a/tests/pe_files/image32.exe b/tests/pe_files/image32.exe new file mode 100644 index 0000000000000000000000000000000000000000..fab6f99f62f9e3733f3c7150d9125738062f8880 GIT binary patch literal 243712 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%0AW0vvG%r^u9YhLn^eK7 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 G3j9B6dWtFl literal 0 HcmV?d00001 diff --git a/tests/pe_files/image64.exe b/tests/pe_files/image64.exe new file mode 100644 index 0000000000000000000000000000000000000000..5f426f7f26598bff141eecdb5a2ed43313d47643 GIT binary patch literal 265728 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ßM&6INHh4zfCzs`nyaz7xjE)k| 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¡?yhFZPAjWN;<b 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�_Pu;%n$_qeQ-?x0|v-d-`ZZ2e$x~#N1SZ;Q(+U}_S6&O7+1!cC) zY{7@_K8Ty{cnE*@hFAGZ#hfb&m;^c@c|A!&<rl>sq>}}uj|ugcvWfj{L2dljZ-&8j zO2*)!=~?Pd)6}T%SV#dM+BolM<)mQL$@zJlI60342M^(Pxec2pH(}e>Y3!QYjUBbK z{o})WCU~?j1=zvJqI$(YK?ptS{0zpl@t{$DLkL>PG9m(_vkx^N5+{%cP?i30z-B!R zXyAY%zD=vu!SgOXbKo4yMey#MzlP^ruoLZWaaZ1Qvs3uUk`L)&^M$|HvJ9Qc34F)z z-dLUj1OQv`hg`6ACN3s#3m-D*Vm*`$q6dydh>Nl;KHlpq3HjoaB<L5S9_u)0bb6OM zgF7T?=+rv{E=hs|)hU1nS#NNytgd2tWfhM-aRidz-5Yp5SXE%O$Mxvj*r)$`^C}hu zt;}52gT;j$)pA|plU^~xBs?2WtgbB2>c+CEa|=2KjZadFYxy}jzl`VXobs{lSMED_ z0#iGt@WiPVe-FMlPWi~ifcqpJJxGIo+h?atyYSI=1m9?&>2_95XzMf%Hfy>Kea3x} z3~HkZn+5_ek6XAH*;u=Sv^{a|tgVDo0PiRUf6zn&(rM-n8w{`vywmd%k739nmK73S z2i5{n*$PSomqOkjOgag%DIHaFvW=77s~U)VBVXH%EW>2IJ}Cb2nR_3{lKaJ-GnoBK zIh6#8ZwJ*bO%ef0?0ZEZ+=q>k&FkRN5L?q$>&o$_7<)7zY?HklbWI#_{_*G!2EOZ2 zbg%0vb#J?bATjt=GJECiXx0l{={oPB3Xt)>VL&_>S@b%te?O6a#uu=1m{2LuDsM#8 znucZqn=rASu^ut#7*IhK9b>1^#CUDsB>U98hk&}fRXBjqZ8HFrzRowkT_2KA@=cxl zuIWMfJ)U|Sy%dz~R`}o7M|N4A67^1^#2^;vSo`DQ{6*NYIkd)N-VEdd$If%c1`DD@ zOM+}I;7Zqd_ZZ+=fDHR{-=6y@=uw^d!H6FX5{c^dglKgWeQALAvV{`BC%%=}5B!19 zMS{LX{Il}@j7Wjx=fJ7eQWciLAYgxyzT~4AEJ8kW{z(uB0fB8!lLWU5@=1W#uNv6Y z#!x~Cp@4eQ^&&xMp>6gZ?ejLVbWvKbtJGEUQ3oNJNjWm08bAYpOUXCMTP16N75@-O z9rS)+>ZxC<4(ax=@}Rf<4j+AUOf)7SG~hjvY>ElVjr8$V<whQKVcXp6-3QIH%-^UJ zlAmWgiex|lRK7DdkdHM<NU+O-irV#?WpF%nKco{_=kUM(X7x$Quzi5)Nm_^tOdrk; zD{sKy^eJ`keB&GsO-dTYUc}zan95+@JrEdskcHD|O|lYoUbjAEmLMK~d#JK6BxFbm z8o?r6f?pZ1)x;H#M)3OU1XBHi<3S^fYh!&P#WVAK+W|^eT*^`XYds22r9h%cg7S(% ztx3Y!W$Sc1&RYc<S5Gb{4MPkmW@(UD#DfuRw!l7RyE%y%XM`hf4f|;WN_aC6C4#e- zlg3X|P&#^;MLhJVBO`+!Hij&cq{N>=R{WF1qhFt1I>i9V$4n(-vOmyq_G3V(eI>s8 zkfa*eP{49=P5W!1(Zb?t8z+~VSXgagYI3kA|72PDoPdzSihS5N5x5cTw(Uq|Z-@Au z7K{?LiwPvon;6*aR|UNg14vZEL|!@ir|K=7h5vBOf;_*y0VRGcxZ74vBy+{qvr{!I z`hBNk#lNSlbe$yx@9IQdKUsd{4f&{())R*wm2|O*tdrU%jF|nd)0nIX3@Kgd)6E47 zjTYutTg9DsrH=kV8v_)+o!e=l`wx0EX>mIX6t)Rumj$fyjcKRBCPpUbe9%Wl$M<7& zud*$o2`Am7B@l)2lO{4T^2rufZUc(H4<Je;Rs<=4^#`m@dYjmZ1RvrSV69Uxvgk!J z)&2A%3!lU$Svg=4x1@AXVg>4Oc3;_9s5dsi>L2&RM9}W$IJwfqVxx_2?&4n@H@RsX z_dR|J=k3@sP!e4*w*|+VU7tfR{e%5e`Z3ls7_6qbsXEin|L^<L?La>8GD&t7VW?PB z9*39+{SWF(jLsy(<`}eIO0vTx69>!RfKv16q!KA)cp7LHWT^!2k@N<z8f3w7@n1Uj zbgcOIZ4ckKf1jd@ov@lZ8MSSueJatE-J)e%e^ibn#=bIfnw>5VFRtR@6H7R?+CtYQ znLDmj^T4rXe@ws6w>@vSN^15UlN%>+k|cb(C+e)Ps9hyVkUW8w<A0v)!(z9>|Gu8$ zBgsBPe7#Pz!~1}Hp~PaJdVg>pu+1!J9i^th95O8cq8F$J0GE=l@_Im7gONq0egpqr zKuEX#WZ^(#N2}n!iavc}=|oBE7(OoPKt5y{NF7H>w$kk2(5V$1IJu04M%yQtmrL6a z;Pd}+OeJKm&y^R>q1)voE@S1=J^_-rosO?cd;xo15njj(8z1h^^{?Zj$U@1}$M||f z3Bmm#7@Qy8ohn8to#?}og```5Fb399>cP3rF(GL2bVx|q*lE?^>Q`s>IgOIX^^#Q$ z*#7M?1X<)!0xB0#FvFMNJ1^HIvdRGpII%q2p!mZkK5vU)rPaal<px$-?wt>18)_Q_ z25=l&Xrj}VfgS|<qO-T7o9D=CH6>5aZWy$h2pNRB9RW<$k?vmS(9ZW^Vg&BjxshVR z@#6pC;*tVkWS{9X9Ig5YR8eZ=bW`$49{2B+Uw^4N>p5w73Iz-ZVgv(v0(_ta!CrM~ zve4i<&18`yydDguUsUxUCu!BFfC~M5cRo2nLfh??RtE>?mvL}@*@K_k_-z8013v^9 zA0Na0$Cd_qu0w_#FS}%yPiO{*8eu8rA-Z2CMh#S!<MfX0P0cba5CgzmtbF#UJP&<p z{X9{4BS6R7t+G$HU$~EyOmB_)?(>^42G#G{OHBcov(+mf5;p~ijem*9Foci*i3%8j z%>~6I+nV~ZHn;$(#7Nm$p<aAAeG38=5fuI2Xh*u`RtE=9F5}?IWvsTkI?0?gowEdx z0FJR*4IjVfiG**`pI_N~vHzu|25pcX>QW@()7t<wd{+Qc?X(<-7@4Gtx0iE)$dF>k zAU8FkUiE8qRK8&6`=Q1M$Nw|=8T=)Yts7Blp<|~A={*1-6v(_zX5U%?PKnd=xB(vN zW$R)D(mrKFN<YBFFNBnm&;rdSKJQ~)s%8*AAq5X3%l6*;k)uE8j%{^v95}gxM@}wd zrQIpp>10tOF#$!@k^TSj^~30>H|kb@woQ)V<(KXbHhR!#pV++d1nPib>r6ZUzp#B; z|KNJEU&*9+Qh>y^B|s*1N9uSNY*!zG=G|*@r#csCa3Lb2#9H@@8fp;ihv4R*U=?su z<S_W?nFg8JmkQ7z{Wxb2dt4u#ZDNI><>fQv(*aBRNF@t`&N?3mROBZ<UT}Q|Tyf7$ zM{WU%>-dKk8+h>eB34^%*EoQK63_|rVC1Ay30JhK*D~Dvz_Eee@+5hV*S_MS;-#K6 z0K}uWke_sa61B6oJKJo&Xg~^)NkkG}jwYofD4oCjX87OQsDMv)a!$J#utXOJiiXb@ zUvG$kW>&TGlVOcypbq-I8^7&p5!oxb?9ByNh{#M5EYV_F1%MK-t%}+W(#Gv`4{NMy zKd4^5ZI8Zt=ruXM8FM?gVWm0WzkP+yDC_cM-`0-;5!?x2-`zJHxr<}=U*G*X>6TAm zRwKSkq6Dyc+qb9Vxqf`2jt~6AH)7D20zGuHf#3Mx7a^T40gS*116l>tbh{uYZYL%1 z`nH4a?~J)=?AShq$4@R*>X6jGUp=_Kv@^Fd@}NsDBtsyQ(~r%>xA%FGyPsX>Xx;X< z$<poq!RfR;P*JaYdD9QvdJBMC09*$E`~Uu`JrL}LV6O=Fipb1RZ{kH_pKa&(Fe!7u zev-0!wI84#FDtFP3Zk;r$O4GX!$%Q;lCRkwz`Ow0-+jZ8>rw&i(o0Gi%%}o?G@4d} z3-Np{000wWNkl<ZUj%41JNV3jd0c(|_JI=W!kJ0D@xOcn-ulrm1MQCTkL$;RKj>oH zA?U;aWS{l|KSCVY&-BIY109caz3rPuZtqr;2*D1DtNYF)zaRbG-f+B^>FTl?5b)de z3KZbaxW``c>iNE>{Xcs9T>$O^@D2cY(_eq{9s%|WWUrtkgkC^84X^DU_9Y&bGS^HN z#b5VE?%2&5fJz2U{y5MoLZbfM3}D~g|8V3$b(Hi-RP3UCyiKIwX9zF}Z`z9p-t+l~ zan-q(V|>7Od3?(`(|F@Gm*e+8_GNS$E%HT3ela?FDA_tD3GX}GF6it>VYbT>Lm7|U zA8dgQ4@M2gl>Sz}t4FBk;~DPec#|w<U(nyFfdan__!Riv>>xv8X0WbBgg3nJ$~{ud z4EE-E6(LCfX@2xNraqt?aM3UCH_cDVs*p*tCdW5pZf+}9+ouN8Cv|EPJ)@BIk$C|7 zzx4Nq-_b7yulhfqaha-sp|*WSCh8dcoQw=cWcc>W&cd&}<m{pH<9IX2Yj5~6P8?l8 z-i>EW7C5Qdfd_O1;Fu&dfoM7EI0(+1o5t++DLj61*~m=vPx6!fMtVu8&!M*M-=dBA z?tGKGAD!iN8c}YehW2g0H;WkPB{v{P06f>ZdI9>(0Q!|b-1l#4hVYtqy*NH)7zA8i zvjIlpAy)kLP%GpD+J}mMdh9FSdzgdW>BXjwRo<86zETkYxG~GJegFP<Pn;So3cbPa zfSjz8kPAMk5svYGM2^pV^>MuHf>~TXH;F;Zj&b0<Keh*-cyJ!?zWM8DEUb|HKH&F$ zvF)_}-b~UNV)SPKyLLIK9sA3*bH0s7e@L8PVH<jzB-yCxHsX6?a`Ezg+{*gmz%dfd zAa^qXH)pe_2wv0E`o8C1{ohyYkzz5iAcR~lC;&9fh|J&9BXY9+z>ABn-|F_c(QjgW z6L#+0ibiX(Z~y#isdSw`_F)LF{o>y~e$y~Me$_i~5k1l)0GR=x0FrJ~Y*IUD5RGk_ z#9#c-WtgrHpEfx%eCb#NfBxBraN_t9TFVXO<wBtbKJ_;~hRrk6$U9vu9$nBRp@?As zGBI^$?%0ai*)53~fb&V!=$@!o1D6IcFY?*HZsdo!-<Hq_PxX2Rke82c&(OL3ZM&gC z&mgZF{oQs4K>nn@@ckFAzx@1?2=<nwa6u)AqZRUX0DPTfksDbP<D0N^ZVJuLQsS+$ z1N33Z0k~cS`@i_X#|IqsL;2pnxLNvxAEhE1*rB~92i~>`Kf7x?{`7k<!KRv2Xw<*7 zQNw>MwsGIlRXjGof?K|Rq#y$brZ-LCB^S=&qFvLteCHPI-dw|vy!VSZaqw8lQDXE; z0G@2DB8HuF)7UXHg(prf>*Fhk#meZ{zdxQ%0lXg}6|4V1c-DjfdHBw&|B?%k)zG>9 zZRbLRx{3^Ve;2^2i^Y@r()V{>d-nkV?=L_2*|)!_AcXKygrFU4K<p+=g1An#?*o1% zs~3?#5edq^!(0vkO%tsgUtAIwTXzDu_TT>R<F~BOvlZY+*scbD(AR>Wr@Kzxi6`dq z+JAZgfBfASVKN)|YLhBs$Z_#hhKr}R0N8?`zhY;lU3s^Qtb9XJ75G7R`AB%1V(@z# zJubW#Lp;s#>?7Fz{qf@U1VV24Z50m&e>{Uo+UMfzmBASF!XL`*8Uo;5T^B&B{P^;u z5`Zo5dHp?#5Ps_SUU<H^(*=>exhI4HKztg>A_6&(xEjH^5|Sn)kJxtm@A#X?l9%>| zDhvqTCQ=B=PQXUoKAH)L1bb*?Joe}byzWyO{@|4tU~?vsN*i6WT8-=sI{WE22>b>( zNszjIOz4*Eyx)TF6o{#W<6-m+pQsNlf!GD!9=}b*;A4|{0$}iW5!MA5{7nFhNL&SY zN(sQ054`CsiV%K$|MSm>$X*EcmVYl$P95ka9z&1?I6<mq6{iHbX&(VR5Rdy58G17^ zTzkh~J$6^h2g4SZ+dlc-76aLi>d>PoWxCJ9c(l^QaQMJcy#AA2yzvzm;;e~{u%x<_ zj~w&>aHJm8)9t+pT#nn?KmJ7Q*oLre8OuDY2+^z=sSGJfB~7H#q)DZy42?uZn&(ld z4AmnV2t|e{4MZ8sobmg=?mJJLefI0@ea_kE{eAwQ_uhLw>mJuy*RZa&)^&YXnqD@| zJzxH8v1q4k+||0znw({WDPT2bszTqMQd6D<PBzf!F*Rz}j>Ww>C8qMX)C2PFnilus zRIV!8wq}smOyPRZ=y5xQzdUQ*+&a96*K|pjvwS)gb-KB>*+I*+7I@2stb4^FnXe9d zQ+Ry7)u5X`gL7-It7mgHKlrW~`XZrso}QIdQ$>9Fi?X139s5K*XRUL2xWn_-eU)LT z*0wS07u<H8t8m9_b?Lrst4(c`hK#u4x=he4a+IgWs6L^CXNKkPkrX%HQ9G`_cfTEB z9`ipN_O?A!Z>V?VCcjaoU*m^2D>&BE<O&|X8D=x=?7FGei|<S?9qKp1f6uk(8EM1# zPb?K_7^X7$ivWMYAnu8L&!($m=D*I!Oi*>a>Nw!(#{GTsj(J{-Kb$ku^mLrZo0h>1 zpO3oPR=%n#6LqiAlRVupr}5^>**LIX+*N_k=gswatBxfiaxXk`L<VA$p=}YZ!^XQx z*1l_6FPL}tY$R7gykOfx!$-exs%t(E@EYGH{@C6{h2f&h$DX?d*Ev43&mXoyOIcv3 z*QzFssJ^uXO?wcN)~TemOMKvlkFxlpsAzC!WKpfQOxyjU4-=eHExFmMS{jFY<V~Hl z>r74Qt>-TM#oIb0i<)<~-YYg(xM$9$jhU7PrI~!X9d6az?;YTzL>8WOa`5zc&}O(w z_;Mflx@KpY4|TGFADV{maPGg#{cw)hu|Xn5Lu~oDCb~~-_>Me!Fyxk8f<UvB=X>w< zzT6%1yVmm^c~w-jb<iE{MsaEVr&&McAn)Di5g)EKXekG5)VlEVl-bkj{P<trciFf< z;nZu1L;FqypR}{<W7t{0t>EL4srt^%5uHaXtIjJASy1-!&E>xJBjow7G<oyeI?~kj zW&f1kk3D5FhZ$(LgpT^GevorVB%<x&`5mS^lyc9vZW#AzM9!`ror<k@j_u}Hy}uqU zlOfRjI9jS|P|482W_`8g&u08QMdhcTMjCtiM@iFcBC}oQ_?0DYK11h@JK1uSt0MAK zGyk~LJ+&^r-8EtL1>L-#9h*PC+%>V@-hGha`)lWF^W1t@728f;x8=nE{n-&F_W3f$ z1N3xWZ4+~s5R)i~5|)^n8+2yV%5AR31CP4jJZCe+HtdMI^j`O?AKa9UM!H1^ZZZGj z$nn$m^3<5|F=G;Et+mUo@k<46@p1FNTw1VtT&13O<VJv%wBYG{!E=i#|e<UZ1- z)2-K6mvU}fNr}02sKy8OT3jk|=+4Or*PoS?zfZ1t)O7x=Nx_?t_|E#v-kCI!b#0xe z?{IUTexBR(D-mPLPu}+y7s$K5DR|jWZBD@&){Ei~#m^i*;KUq>)gzWY@b}+;Mag~r zQ?uYlAv!~P3brKV9d?%yu^4<kK}e(S+`OIRN42>webR0(+CHmC=fd5VpntDsS`W3b zx(1|t=<_;EZp(qOJA$2Mdq0ox8arw0xN}8SUml$8fAaYJm9g6D+Z&%9Z89IH*msJE zZGn^4vuhI+cI}baK4a8^oh$q_o2zBK*DiKhYnG%XoUN!NHcz{}<@%%<!lKqkt@9V^ zE-aF7+4TD5=Z$sGQZ5^9^&iq~*mHO2Rr}YPH})-E<goWyLO<=d{6606A|*M;SIZrC zJm9(RW*-kDC64yg_KUA}<g}_f4c#H1+^<waegWrF&s@y$bg?}r`mFz&d-mJ>bQh<s zSM?rMBJQ-m<<(6A+XEB!yRT?1+T_~td1kck?m9h#xR9a76JA&3o;NbvBwh01%iy;u zgAAj8Jt-60YhRT4Vwp873>DgAel8F->o<;HG``Md)U(9sFt>)!7mQyer0o@m(LDJ? z(QoKj{kNaZ^$oIvigR`7?^avCx<z6?+rRkue23VOiU6U09%B~V%^O+xbcFwyKGRhc zddzuuP<q(bXTOMdyfZc%?mFRmvhaaR-f~Yz^xD$<HLNye*t_<YFQ0yBZqew}Q;RkF z#eL9tGqA~Z?~Qkr2OA1wlMjyXA(UJ1R&#Z&Qeb^wqZyUC4_9Oc^_~}Z>6QGun(NN{ zS8^YVNA?~r-t-HZ^GEi2x30V!b;2Y<;-Sjt;ZX{&_j5M8?+sIyoiMJ?b2HszRT)-G z*7IA9_*__YMEdnoc`vCDip5Dac&u3_s>+|^8)~ZeQ)&NUnl8pUJEu<iMa-Z?xyF3) z^NW^w{K1p>%42pupYC<R>*k|O6`9cc+#cilDE1pvkQ&u%-n-aI9TKijEM;y#J-;&G zi?5ha?UzFx^WM)@A64WrhWq9&-uXR2p<2-FabD)G%`Fl}%~pFkA}^*y$eIs)DIp-R zH_JRUUf}$3a@M)8j*&Po7?8GXOv=DXg1uAQ*FITwGROQwpKUL6I^`zC?!MX>?7K$J zwusNVckZ?*(NAA2<qAg);?6nKrYvL8VZZ!b5BZ?bb5VN^y-wDizftx4%DKDCl7(c< zhHFcm_nOP!rZ(A9%3HcxCuVG>=Ebdp9=+)2-7)6ggi{A?KS_ISs>?5a_E~Xq$%smS z&XN8*RZp*1<!GKXSaA7aNcu3Xz0wJfY3=Q))K6ttynX0|rq@dc`Q$ZTJT{@Z<$>Cp z(s^fZrEebhYmjYx^sP$kkuHOGH2C-XcqhN`jrtkC4N9D<PwV$ZdnG+l4E3{=jpNq{ z<@cYkoWCR{(J5?#!pr(pMe=-Ah6}}A)jjb<VeqnI{o_Uc4|>UD&N@43$(pA&Z~OGx zyP>bwJ#E{}wP>~cQmA&SUuR6Kw#%JgFU}P2bMUa)9M1R{k*l+MN%qL(+x^<2)%biu z*tkCNp000-Tn~gy=#xCLPAK5!+1RNrt_KGBPZI1iM0w+D^Ei%3!5Hq)%WE57?)MTF zy39H9VyCKU_5NcU_s@t-9<tVF8lPnEv`yv${4#e^Lke1F^i(i^Z0OU!C}d2Cx_Pk7 zfu$l}te4GQvoD&v@cqndrSAnkIBUOr+E0;F5yi>c?Ed!D<OlD@6<#EkzJJh{<HmP; zdT^oOrGDFHJ%8*a(Il|9U&@5-;ZCV~zCGd_6Z`Brd)KYBag0y3&LFjtc@u9~dRLn( zZPfbsY3rC53a3u_IQx#REq1N^QsZiA6aF$mcBu2pWu7NLyu+3t|4!ATin~>h*4aA^ zoF+9`b%M6I_aw8QW;NG*RtVW~L?)r){2<-AS5t<1nrfEL@KM`hT`rMVWqtNSso5g4 zMS(|u9`|MHie~57A|;u(Yn7~fZ)m(&{N>6D&(<kngZQK?EAtEK-vwCmUSFBq5>o`m zjyO8qYx#{?k)|sLtI5q)4^g`DG<H#|f?AJL9!}OHjvZSed9pU8dZL2$mrm*HH6z_h zUM$#Lc}C!Ew)=<6nz!4PU8&Shlc%r#{Jei_%~PZ7+TlH}jo&i3{;I>79!f)xc~4M} z8Mb}@XXW@Uz0_kyR7*K6H8FGQ`)W!5o_5dom~&FEMt{6?B0%|bfS3O1Pog#+YXUy# ztMGS3pE=W_A^dWO$i%TL1Kd9xj}`9ZGYY<~xG`b0`vJbXS>lyymnWFX@JSeJ8q70# z9qnh-crDrE<J7fxg^%wQFj~3tecAj^&)x_26S3P_&^j&4IMP6Uu=Z&M^WidFxt19w z<$;z~(NQI-iMD-%4{sLw>^(+DRFnT!&)VY7DjWCZHf?&Fh7aPVyq<Be_^nMILzqC8 zKR4iUh|cz3ry6B#KET)OQ$s?$>93yVTZtmGpO?+dcYUqb%OgDhO<$3hIh>Eb;!*yQ zmrLBv?H|Oq_oCK=oyA}HGzGLY*FER^GB>mHU8L_omBH4>H3unf=_@E;6nr%rBc$Gm zy1(?4-aPAB{}n3qZ&5#)cJRx!kNJMLw?FMAxBGMInit6{yr(TVW?vH?5mA!*z-&Zk zYNf~3wje3K+gl$Gv2lJW+$&$fREZOx8~Y$G+O6uXn3TYr9dik;?$k3_NqF{_n$Hb$ zyqwEoRiOWn?`bqTpF|&^Wy4f5(_uAhW_7l=-<kEHETPazJbSR0*=3Hl)RBAd7L<ye z7dve-J+I2@Y{}vq{kWllhM$Y|_&+|x3rEd59*z6fUHN=Lu6WJ$lJ$LZ$4r0U_tU0R z+49jIm#%0So0*OHdHQ7qRk-lIjr!DPBm0cTK9)vJm)oCBP&s6gxT3#8?WR(>9&$6n zP1c7`ZSSe8-zS-~v5Xrp*RoCU<lFPAp09>)p$<bfm<&eP9FNY?tCx%s;8R<Ab;ukq z+l9deB}Qhwj!7luY;zj1XZq#7iw4Pei1B|6^lX`1mBMRgP0`bWhniWwMZ*+)iU|h# zI4LgTWx4#4>CawY7q*k({}|NxI@0`ZN)^M$mwTtz8GcrEyOdZS@i^?$_0*nX$4vUx znJ48OzcXvtXZO!h<7&5lD%%u&l*dJL_lOVA<!XKEs|Ju~Y_Gx3Imdh=PCg3dtO=a) zaBRVs+Wy5a_MV9~&t!|wn0{#83!TWZMxu)sH7#-9*qd{5xc4?yG|xXWw7bM@sl}gW zx{0{@ib+WYJd;X#cu=SA)|k&7*>aXa&11r}hleE>Y*_uQ@z|?KIhV_tpU;+GaZYm_ z&OG?;f0x0P_~~5Jk|UBDqt9~aP*+~2l(opnTWbnkh*2`4#7A3ln8Rx#oBcocvrFY? zVc$gphCVX<(Q#Ju!$VgL9r*uTBfn`-gu!Ux19~G2Ls$M99i%z9aqr>*@K^4Oo$|eL z#B>Himk({E`<mZ;^1phT;vg!VXgk9&Zqt<LBW4pC4;hOfTz?e8Ru4zW@}US^HVAvv z`@>(UFJdfaVc)7@2+|sYy-NhMcdHHfugfBTkAmoNUs;=@+ZSeMy3cD&bDoW4`&me^ zo{m_{$;9LFh%^$$!L_5XUq^^&_yvI)g4m<hA2A!n5T-jEq(cbM7=&GF{o8ja_9f)* zfA2UMxCnW><#BAM405~{Ak%#=(zc2t*=`o%H%~*1<s?LH7>7thhK7+u!!QJE|AK&} zf{5EZyPGcN-kv3b@Kx>yFFAo?cd6dO{|PFDbi_qRu3KOuY_9N!vOOK5gp&b8!!Dv> zhcvRiB$44Z7pYF-NZK;9iv|m#!DJjF){Vh|H6vM_2_~IMu$Ab#r#AxBKS;L*lMDvH zXHj3cOZV=up54>`uhKByZi(nvD;1kDrt;aN3>UT!*OR~~qlFN+*X|g-UfSmO<rVE$ z!RcTn6zpAu6aI3@WoYn}M7GB~lqT8WW_coRUp<Msnv=L)8Qb07ebnA0`+4B?kNvBK zSh?_I;3+2nXGwmnpV32#>ZHF<^vB1{08`QF{zf)ax9eq3a$DIx!EqVJ*{VZiqY}n! zkOlFc&1XBUz-YQAnu<rPo!c#2?m;ya?o+|3U`3)q0Vn)qQ5<Q2`Wxra)X?x<s=sy` zht`ipxWQ;v?^7HkyLIb;-bj+cFp|L#_^S_qH_5<Fx;HGudoHAwkEGOxp<qF}m)yJr z7aNHvhivhK*6pGp7MQu$1k-%iVTzY7Cb_P_1P7wQMh(K2iWqGo3t@{znB$@Y(jkmC zmd1#+^B|-%+mk7;<j`Uivot8<RFEQyBXrQxbO#R~w6Ll1W+|!*f^j7;5S7{PAe+?9 z^R%r}h&G!@*F=;4XzV8&6|6lJdzKEuPL+OOWWdzHJ7jYGurSjb^OLt>Zk#jb9JYh_ zfz1#Nw!n<t>oIMo0j7Aa#w3^J7;mqEvDT_=+ex1wu}u#mn@A?6au`K28LlgVVar9U zu4HXQY4i#dA5g`aP-Qe-FUHd+kJ-eumuwQgvq>9yHc15OuF|ZHirPRnis|3fH$ust z25Nqlfnwk9+OKe852TO!VL=AbkmQcJv0E`e#tGW#{+JbHPBa)}+75k8@zB9UXKkWk zDaLMA!MH7pG0)S0=pmVqEJm-F!ASl25LzXU>jloZddwb`sb)AIvko2Y?QFVx`9zmZ z+Rn2{WLL>1C0O%p(s*QUlSRDMR79Ik#9`7a8tX`}Mj&|kuU}=*|GV}p6$C;4#BQR& z7mCOCf_u9f*KglK;myks+rJ630?jaEmocXLti|MQM8j4sP=5x~C+7PYvgaaJ$|M7M zrXPUm+qX`;qb7eVuIAdKn#z9t>J^*HlI*)}QqH$F$?i9sl<ld2M4K6i-8hBD3DPSg z5!RmWTO~v?_=Wnx!0+0xdO8$}1%Z$|z8fm}L1=DjWK(f%6=p}+LTukgP=5yXXH4~8 zgURlzFwseq`owZ9@G~JgG^t;xcl8UB!3dJU?P4$7JmrR(JSWtijX~$TcWk<l>Wt!W z9TWtpb=#y2R|%v#cG=Z9;`>o=B_#3239_+=zuMR^vZui-NCq^f@pPZbmlPiWOGADD zRPzGR`rrYZN=Ob8M;tIa+y<hd3=JDF-OmtYlR!3!)u)BK&A#>v(kbc>W6Ts-8Pu2f z;C7KG(cp?()cy}2KCtOZZXily*Q4aH0g4XjAb*cKa(vW~?jnH{M={VG1IPW;Xq=c$ z=QBa`jqbjEkmeivXsiy@`sKU!>y}1iaZwmlPK7|_ctCeQJ70Gd3liL5dEx*fDo(&6 zJCwCa)4$rJsorZL9cbC5Q%+jH*;len4P}0)BN}Q8Jy4!xhfkkAvFXi=XDCZ_L;hY3 zltfvRY|_wNeU`=ubs8UJXpEMiXPU@#lO%d(BZ2fNhUOc*z8psOl##)={CBx_43?Ft zSbjE=+P|l}ukrdEFF#P43&yU_*wbZ`7+FY%Ztju?YhTGWZ6@19_O$8Z9yFBtqVB9W zt3!_)uHnm<FMpVts<=2Aq{-5dvwaZ?!}Lgp<Y~Mn8XUxFte#GNne>PmBgmfa(;5C< z`?uZ5hcnTz<w_RToeJ;nYrH<k<ab-|!Q*5s#KNpFE10oK>seXIhHdGxuO6$r`Zd`k zk&Vi@cR3JE7k1+=(Qt>m9hGUexOeS5et+D%dXB~h8=Tp{iu%MdR(DDcZ=ku8I?<qj z4A%uT@0(5bh-jcOA(HwsBZF`G?_GNa3=Lba=3@QnNW6RdmQ9V1THt*02(&YIkzIAe zT(YUNBexI@WRrr-*zrL=!lBDHQNL#T1UpWXJ#DED#l1^`Xk=(Ozmuhbu}PKb&L}## ziEOGpxDiGukKcfEM-0IYUxPFIR?=8adUbd`PKT}{8kQ0bibR7H(J%*bRx`*RO{BSo z@OSO^tK-7E_B7nD=fU(;BtE=<&!)<|*P&m02+L3Ihf2;aETr*GBHFRbCXrng-*1IQ zQO=mf*e0e=c<N%J3ylwCTOZaOAR0o5h5$5H>_Yvy9pBlc>Z5kJoUs`fQ!H>PojJFo zv0@`#o8ofT7NW_7=+Q;qo@F@dqfELaO*BYQf1XKwVd8h~-`j8=zIDaee(My>PDXb3 zwTtyNu*r)+a8Vj^E)^sG{Bfvd_(CS$4H6^=iGwy!iFSoJGuL3oX=Z#N+d6@4Yun8T zJiHc$`&A)muH1`;a(~p9>>xUJ<4(yg+&Z(9#yC%0&v!vpwizxauOV5iqA^<wC5JR{ zA<-BYQnpZE(5L>qoNTQc(I7`O%p*C>`mX&U%@x?wa1OqAieO3n|NQy$AEpa+*RU)% z0O~}8VxlKx4{wFq5z?zrk_E|NCSzN@$hOkB@c8y&q9Gg)uI<Oo^ZRi)Bn(IVt#Ia` zF;0iAp}CwM&PA_5Y1|r=#p$9fmTCKPoIAV>XCr9tbU+P7Vd^N0r|~=18U=x?kh^mU z%{vs3O!K>M^*^ko62Z;o*nPJIn@%432OH7x>IKSgU4hSuXsjsM2aTh9p_aB2iw<pt zdIZfif;K`Tz#QWK)Te!DuH>wbl(<ybY)QvFxl9NQOGB?;k|3#+0~v{RIAkk}jP1%e zwtF!OLNstDY#H^5Wi+lWrGBuatL^){=1t5Tyl~$VqSFf8NDCbEU4|?VrSIAw*?I+G zEf*2gREEu`4x^^w7LHy#gT1E`;gEkAW+kyOEse*zvqzwNCLGK2L$Nex4;F<xKrMI+ z=J{A+)@El+)(M5MOe%&?rGH#vVgUw^JC1>4j^gJL3=OG}QZL0X6Y?=k*aI8XWD#vM z56LuF%OYEO%wG}t0kq}`Ql@nSFDc<vpdz~tV&>q5VJm3N*ofo)%fD;?kw@1M@$d@v z-@k~!#xm@xKMSARzuBZMmou^PLJBsN#$g>d3aj&?pnEtAs&+}3s+@>P^NT25z_>YO z5Ed)Js2N2NnsS<G$j9KZ$1z}Z4oic~l5+ey=_H1XJAnbCVliW)9=vsC;P56f#5>Y_ z)O{hcy`^!?PYx$&-9~F@cJ08lojK3nP1gY`G;ZsE*Z#PtHxTvcD#9OL#=d(O5YSi( z|N7r-l3UFQxLi99kL&qZwe=1b>o;SbdL8B{--76(>zE;1jj0PSV<JmKDaMGFU?fY! zDVE=ZM2-;+*^pa$0YX!|=o&OOmuSeupyA%oo;M%C`jZf0L36e(v_^KBhYXJeG_N39 zd}WCiId&~g>*;n{k7iT)ZoQY>UW>RV*AdlLje`#^A)@6H!tYn&(EUr;Pkq~uXmPJS z35)7X$gRBx=@m^_u(Sd5)b3z5LxVyMrpr`e%EC*SIIjX@XEQXM#fa&Jn7ir{ei@$! zL6Ka@YhGmU4Vz3d7=HqS!a3+SA`JpV4`b8_eON1wMbO#_2sfLK7#lGpZJpbg<+U*T z=nfg1<9>3Y-}c#W&yT%qMBa-=<hIx2`15)sJ-&{J2bba7Sb}YLPQ&5G30Pgt#)iwO zP%v(RtRB&@ya|#^?_w@PgVIgRlCOdIk{ZlXt;U4871(y<E-u};i!;?t=xT2s7HMC? zsF}soKhAvBEk-Vx=sP?KKm9^~Om2V=mWVDa-n~l1aG&uM;cwgcqw}KIEsdvNHY4|W z9b!n27<;s*={&aA7s2IL0c>lI!h#Sz#ut;IWd0BeMnr?|J*>35hXq;<n5%jlX(jjB zIrqNIW=xi>MA)%r_PVJ0K88;_!|J`#@~SS~nqEXSF}l?ygZ?8)2E*deYjEUudi|p| zfBVe-LCL_>XSae9iSLJ~A2YVp_bwOPZl8j~_2aOr&ccREDKMyrhsx$wC~a;*O<mU< zTh6cvnidV+bMBfu4=_W%3j4F0*?ZjTmTsOaudK#c@$(oxllzq>#zq~(z^^jsJ@m-G z)P5c<0r$&AL+@2o?P)v*?>mKXzL^J`t2wZ^oCc!{iO?&H!J6$)plsXHJ;#<cYQ(PC zd)?Q}8h5?h9qh|!V(*=;e)!cU6+>0$8pca>*{I+8_X!N5e$anpD#;-B-)cXPj_q}Y zp4)DnY<H+RhD}u&*ie}a{qi{IoQr~EL_5~)eAqq5X4-%G=41Anm#3a;K}d2Fd++R( zR<f(55GI?Xu6w<^e~+F;{d-y==~V&QQ$_|EWMdQmBkgDCFt5%K-dvScY<?*fMimLr zJ5OUTtq)wHUtqoe!|pk@hHZWK`RXlq*z?xM9XL$-!_2!&F1K~JdC8g^Y~L6!Q4SHY zbF5ym_H;rX28oaiMy3A8+Ru{(>Aa18X-xa7lEcs}K3L_E_!1j-x8U77nqyOP4ZXwW zc_pn-(Z7f4o6p$m`g`qcUUKOXrYxvrZIXt5Ev8C+J5F@j*kO~u%3w^!f4uz+9kf0k zNb5`1&*`1<8m83#4<BfbP3d^i12*5jzXAGo53wz<zWW*TyyW5|OqZ_0WTHXa=niHu z;{;0&$w2&ES6^nwv@RL&`1W_@zPWE;w)cMb9GkbUwc2qX#;$Ed!@cjGm0Wm4<3kOm zNne4EX+6XhuhCd{1(PH%cFi}wj@8T<@yqyQe+&P=<;CuTcQ6mU|K0rh+Rb)s+Wiz} z+aJToxea-Rt$5zviSrkpz+w9hELvWRg-fo((2B-9^&3RPRifcC&6$|7n(XN;F7@Tp z_;q65-`@VfqE1)@-TQ8i9dNh-wjs~4(f=t-y&hvFwR4qaGgg}3hsL@_sIIPuf_5z| zobE$m`E4v*d>ykDt4WtCF=<|x9(B$6PX6uf4?XuD8$<4+tnvxM6Pw_+uMuuh&)^W= z4x6B7*hqkw4IYoM&aoAGRu8bk<Q|q9G(ttE4(1LmSgL;)N-Jw2wX_Cvl&Xk^izJ6K zvPWkza{8ISz5NF&J|LvzJpxX@ga7fj@X34)kEEAyInn`#1MRR5d<IM3CotLe2nG(V z(B0Gm)2$C-?D7!yevgTU`_MG3Cwgu{W?2p9s$9V=*^8Jwzm#a;{_X9Lyz&WQ<wV2T z_q4uw2S1{Lu}Ry=t~y6`z?10kitd2>;dVHZOsw}m$11NU(A@eM8upK{mimX4dkc(h z8lkRt3-VgmFkkfwMCC4EisZS!z5PdOJ|p7tM}(DqKrpuxVa1(@;J(9wGj9=`{}vwU zudzAm1vZ4X!(jJw=+Jya+x0P)Qrp#N?xbw~0L!c%!pQzUOzj$>p??dC%db(NxD0WH ziod=6iMPMtc>QN&*M36gjgN?_{D6SMPHfM83+L3=u%+?Y{6Ghc0-i%}$5X6ue~e|0 zkFeOf4JsB7p=i<q1;hL7I%4I<7Ho29hPL4?sIIyOnWdNi_VyRIe96wG_9xeT#GwnM zM<fHkytmj!I^+=l3RV#v*bw|227b?=<M{+yTOVPmZ5vcK(!6!U11zGsx4iy+b`2q; z(+p)=51BdM$A&F+Sgd#LZ*M;{7DQEj)Qr9Sq4D7Pu0G(M^A;{iuV71hW3jItM!TOu z&zpEpdb7;F4eC~;H)ap9h}J&x2EVmkdSx@a4wBNMIk?e1SUBAMue6_`;ZXU9fuTj6 zL5vOZNF(0IyoA+3;yu|PgPl*ILwcj-*oGz6tx#q3W<BZ6I?|gp_aM8v88Y9reW7L} z7HTwN(aNS`rtJUbu{HU%oJ+#1D%(TEdm6)xXbjUMd$WS{X4#fjsBe4-WkzpE=jBOP z*tW0w%6ILiVkylg&xOkx<^G#({-e6A!a6)nNN)^$o<N86MvL@j3E3G{vj<RQb^czv zoZh``(s@tm70v(a?foMfbUmMl>UlgaUg1Q%w`pxxw|tnbZ1TWnkzvdK=sWnMa{lT) z#wYJXJ%_SRzOLMh<H=liayY~(HmBJ$I_a7s-tcl}07aZmju2aw7f0Z?NH{Md{=bz| zdzqsr(ZL)%Pdk%yzg~BI&50WJ`IoObM7plg^dHKpBBox3jciU8c{11V`Uz~#^<;7e zr$tqB<SBLIv;^tmzi<RC8$6J3KbpP@KLw>YshlAkjqu-DZIbQGffN(eq?l$9;g6~q zyhR{`(NX!CVr^0kW~V)1=^&f`WE;r;DMvV*pe&F1RcVA@Nwk@caKo_()g6UE?O|Ow zA;R4x*~=2vKnR4}g1>72Dj$Wuf4Scgu0vSZd=d4Q-wwh*6z3BbDAz}d>JUSI$Z}L3 z+kwgxdl73h7kjmSW${a?TNT(iHG`R9@Fomi#%*CQ&)-V=$FoUpD})ITU`V)<=ut*e zjUx>t87x2}VPZ6SZxa@#ID5M!PV7~}txLzy)JXV|uc;tp4T4vVWN{<uuF5Q4fx%8O z*e^fT{_v3b33KMmSwdy8IFWgYuEKK;+Z$3$JeqI`jZ7?=i8(RY7A9`VU|W=TY+&&r zBiGK)9=>|cz_StR#RYri(0IKFkK5W%fB6_r?O%(deoGN&BhF&E7z|OCrz#s~Wnz>J zUV_1UcoSA);lhQgRR2JkT>nPGsjwIi(F0bPLHLv@6f<J*DGa7y9L4QKHYr18mj#Q5 zV6X&)M`=%uT-(k)WQ>lEb~In-BAIj`h2qn3HZxi50~4cWuvkZZmJs%B5{pA2Y#)LM zd*!D(fO=@xv54a9GK6uMOY!45Q4TPp*cO9rVB)w_DE`UBt(e%GT7VUcF&VQ#j>W_z z#aN)GFamEWE?IRV2(e6z+*gLhBQY2f22T}B_z@;H!r(3#Y+HoE7{XHtu{_|_uX-jF zi}LnjPNFkT-@JszyLT~<;%H187@W@JZL8Q={^HOrEJlRE8j(&QHE}a)PDhYm;{$FK z#Ua;EmEr^xTivdXL?<a0m&V{F^7d$VwT0r{Ostr}bOjOahF8Dl*$BcJgkXM}2a0cB zL0juX%s*_8=>aAX-D%i`Q6X6@Iq1l?gTX{Fn2t<}FLI+cbg%hua#K)$A&<R2zGp4< z3k7P!9GnW#Wpf7O7i~5H5r)KrZ(`L>6yIG}76;j!oj6-pjrR8ED67B5=6dlyn7PM< zmBq5du3hpVd<mluIXTX#Ic|+J6zk<3?_Raz%JE2w{iqSfN}G7EN^x5=!s^Kqc7ci2 zPGhlc2iA`2rr(R=n@o(^sN@JrDHizl%^Q^8xr&JMN7*>0DB(nA1)4%D)|2H0gPCG5 zI=Lsdv9a*lGp-buq}X-<#VC*HQ`~G7#gUB(C#8pzftsiwYzu=+$o7&a?12R7#55KI z#p6E{$MC#)5~jq1a;l$^=h8G^C?&W<A<_YpcNs#Q;`1w#b`UStv;Bh6$rGo%asNs% znwU5+VIr6~_O;`VxN&MHVQjqV+yhldod_@FM)7S|iV<leXS*V`VLr8CdN=)n6rc00 zE5atiIxuuIankEex7qyAxucLe><H;2TcMlkkJ$|N%GZ$MxfByG_M><-#d0qP;og-f z+&H}lH3hy@*Ct#_Ga;T({4-`b#ch{T{Bs4xpWP|uxrJiqs?>%B-SsnZ1tvDNh2l8n zgw1Jx*#U0N1?<c@1ocQ~Hnyyq=!LaOewZJ!3A1-^z*HwQIPE+P;|=MUD3(rs>R8Cm zHNX*jvd4t^I2o!zu}cj$c3FHtoyFXp301{~R6CR>ZbebJWq19DDZa$S__k6^z^*VF zrseT4I2#4KvSc_{X2JG+5>yZEhPb6Ormu>?c<Ee<<rT8A3MLlWe^fRb&y!L=hhby3 zVXcBRVr&T)>LHC>UnX9+h+>TtZzSv}gIhcmyqMayuDgCFPIsuS3JI;(aOB=49B3%R z{`zxlY)(Ih3prX#GqDsgipeoPOD3ks#2bDYcN9a$9z{Q)1U4=zyQGYbUkw<QjH%;R z!gIwGiXqNo;#Ht{)z_GtJQH(c<83_s%YWH~lN6^)etZK*9#(;34e+cx0~?C(tT<Q> zNs8snQNDv&ay6JZ_adbVh)^6&VeJ(JrqyHQoP0L+B){wehEW`oi2?Q-k%|GodP8aE z90aZz-*{xRXmqOUe8U`XDPdl{y!^u3w#K5D_bA464IxywH^rA6DK=(FF@oh0Wtg|T z0Xnpnoxk`NEOykP_|jb%x!*=mVm+E08zC%thV|DfQXFgq#gu*_zxlv1S?Dt?9{ht2 zcJrB6-|us~``m$Xk86S=D8{&}p#*LeQ?$OCjrA0tSb3xrhMtXhMB|&GO9M`nJz&pM zPTxmxLIZm~X5MMmXRNaF3fm4QhBcVs0mV*w{}T02>*uv+M*~;R`_7pvCO*Q%b_^&s zVwPP7o4|V%^LP%2uQ_wx()tv^@r~^L(X-ExAHE!F6vttF*GznKDEUkU86F75|LOX9 z?bvuF!_%1JO1kHcz&y7UTf<uLhHPRs`EQxrCFm}j*EP4Z`6#h7tnYlun(J&FX$-|+ zgr=OL_)9MJjg-G!KhqBVvRL8eXCpM7bDE=F54PYvVP~E^e}jg5FR;|Ok@A=9J?8%C znT421v1(0&TIw4Y*}lQVv3{MDPqEz0f2n>RFLvd<^>mADAzS!~O$j+ISib2#JOdc} z*mZCGoMP60y=p@p`Ax5|vS4E6!zq5i@PJp=zmf+PcY5xOf7tDpHut;(W6wv>q`FpZ zxC#aG6V6k<Mn1zj7?JOAp6WGf!zE0RC}rbF!-xm}O1->q$fxPKGvgV&_SL~^cLT+_ zI$*c&IV{Pa%)}*h$(O8Y)C32gR@jjLQdy@K3)HWYJT6ij&i~uK<H)K{o{V3W@z47m zdke3WSFAsEZ|Y0<CA@$~R6Cr=pSp_tGA=<+;pp8${;pe)URn(?`3q#L&i-5U$K3qv zd8q0WqR5|^e&quaD#$Nd{0{c9FJThW&iWFyT^?hxO&j^Lh!-0lz=h&~n(J<nY^pIw z@!#`(7C-o6So+{Ij+0+A^lT@5hzE`duV6_&@pZeNVdXaR!P<5CFd07@gM&~Y9D$AJ zL)eksQlZ#1PnQ2`e(=&qxybVOReMgp!#47X*^p1kg#2N8<O61WVd|S&Sw9%-e<UB6 z^vWhMzB^UDW;kxY_ixwFYxmBaH=YjBFJZZlX!m`JmBja@<cCu+eZczQ80-P#f0NQ~ zYLwD!j3(ckAthnn_y399CZa=l!=7iw<j;W?@m-Da0UEVbkq<41d{CO?gZj($ksn5m zd<Vk##Q6z_IXyf3(SEM3#`dJ_&n9MV%J3RoO5~zo+!G+z#2+Sl5%eO^G!TrRLWq(W zC7z$ch+P%ZjsMUQy5d*SJ&Om}>{jiUP3vHrf45lLKO{<PESo)gBE^9^qh9XT7z!Wd zf!MyNKN9U^n0-&hek%R{v@B@-(1X_|W?j$hEjeg1k=BwkXnn1P{9rvqnNES9`d~2Y zd1h}F`4X6Qv?uSoZ*#(%%F!Cdlh*6mwD+K$)@<#gjHOt*i^Ei@d|AZV%|ZRu)412v z#HPGJUGf<WLx#Hwi^~tt7>w--eHfXt2VrzQX)RRDVAYvD56l{b*&o2{mr?OIXZJdc zSTncy<e}wNG1ilDyCMybA2y-*kO@N8h~NPEMwop)*<R}Gz8J<I;Ya(bc;(NIw8dD$ zILZcEVU(p3yC;Fgztj3(EpRg!EI+gMX4bvOb}6CbSsQK?M`N$fNZK1A!D7UjHTSWd z8ti(XS!V{&{uEyM%>D<n<N%tpHDCg*p_siJ6A9nV>~mPM-+^7*jxm!*dWsF<BB}f* z4{)!h1W|<RJhWMwuw+vR=PAmrXPG?!3}%?|C-BN=@Q)TrdkEk11ZHvm5O>zWWZGN7 zV1G3Zxw3m*7#wzXwiDrE%}^L&-#xA}80^SR3s`LO8S-Hy*@?2aWM&^4ul$WAaj;7d zr8&=Ql+ij((%pc?NHf@0t#~hX4*^qGZk`8=&E-ZACUS=cGJLcM$47fdY$b3yWF@VE ztjI?oNBk0{Jpkjo%inq_3--yu`1tW7Ok;hqBFG-&>@+Zg_PMM|@nx~JOkH_}I|+jt zM;O{jG*s*(+-x9j75WpN+=JG7TTz|sLHN=wBxgmcV|I7>J8qtWTk3w6=8o4dP}kOs zY3{2bM_BK*iGGj>q_y1+14O3>A}lBxYuALr-FzMLgJ{1@#7dltV%BoBrV3NTr3?pJ z*E*r_fN6L6Aq}OlKXnA_Q+7i$(+?`K?wB870p)#un7Q5>Q&)sRWI+x)H)ODb0yH<5 zQY(Vk^mW*4G8gHzUOMj2U>UoxnWutOY5iu<U4B$+6$0;`ga6IbNNl)>BX`T8;Z}_W z8g~f`af{{~<rqUaxIv>+q32kI0mBm@r%}PKiH3~aiNz8NanN)c`9>s=y<LjIQWg{D z(32<MGlX?3d)a~$Pwyb%K^3g8<v`NnA{Lv``bYIT{NtJsb&SR}%S)`EZSd$cEYiBf zt^*m&pun$(FhgW{aj4-WIbNCH=7~>lYDPZ1+|H~qoM?SxMi?S_r%QPJs155p>d|<w zjXl@ia+%GC()vziRTYc5WH50I7VF#c{^&foxon_k?WrKbC}AC~AM`@2@$%&hxE;97 z=64#O;l%j|?D=rQNG;Z_VKGMx-e{0W_CGD3SEuR4WVzMnqO#T8&b{gE?8NPc7brN_ z3eTY16i(=5)9C43SUTNh=fk5(F3g&Q_x+#EHJr~s=3!y>C$vD_;6^tNW;$VAwrp#r zHSr}DFEX64B7e4A-g_%|Ry^kIzfdx%#Y)?USiXVQ5{5SkS9~4jj)Y}gOgK{Mi!2s` z_uZe(9n+g`x*oa@qrgX4PICtPu;=h)Z~$S?;1v1{!7<OV*_GmN%dQb#;jhmXDehgI zSN927S3e-OtP`OHZ(+8-9jj?>#>@+uIjQZ=M>H?K2c;EP|1y6rKmMYbQuDDrl;Y7I z6pLo!&`b<ko8pM7mK3|C_#rdzG9g^a7SH>CxqOC?k)<C7?#O)`?MSiK4S|F$pt(E~ zUu9ye6x-v?C#xtP8T4oQ{zvawg|*939I;WGVu%z!tWq$zA4G9aX3oaUo&M>8FTxx& z{lxjg?a679|FhFFVJg>?=5wB$CQ6-ukUr5pP=Jn^hcV$H5lXyym^?pS{EG+e0Ur>s zc;MQAr2}`;zV$yvV_Arzbk26klmkX0FC*5Cpn1*^+P5r8drgPJSE--Zw@slvi(|)b zQnnhgcK#{aXL&|YUvkEk2o>jen<=<ko{F|R7s$@i-XDKm8iyp=*oVu4UR;`I9GXDm zvf>^Kj5MX#0_{yx+-Zs-G`BFenE#}y>>&Bm_mU5P68R}7kiX23_GHSj`(1q$`(B(A z?|f;5yB1nsJ;z|91sJ(r4hDxkF`VWsu6y<H>V7#YlI;*j`_vNc=aR3_l--*UPQLd& z8iOxvD2~1`DZq$z8Ry@+fH{sUVVvlX(X`*sE5Z~F<j?*5={?>&Z$Z3+G_H_eI&Zfo z?RT4qeXE5oxK|y!pq91+FW<j|e(VmY?cM~PFei-nS%clFws=wPiEHV4wC}?Rg^|X% zbM7Fnk$*qKMT*Azi5CLzmR!&;49A7mdR%?fgu;d^&<wW3lHl!FZWD|p%hIvk>o7{w zd~hye3GEwE!};jd<jdVbb2*!v(T}cG+Fi+pby*U;34g7*xfGiE<oB6aj0H*+=rbY@ zLnoJ`@326uTrdS`j?-wbw-mEi;3fCaI{&h_tr?{>&gDF=g<D-NCP>{w=82ckT6Y75 zCGD8CARntY)L`hOLiGG475xQ`4@H`c{cc4-`#Lte5Dr=Yaul`(yvFUu4|x9psJ`(A za~E-;Z&^!znX@#ekpDw4ZQHkc!#gJ**Ff!a2V89TeFVM$Dr@Rlnaq|g!+P7hERIo# zd};kgXL|C!`J>!72YC4G6Bg(-VYz7|WYn+0m9WI3gx4N3tAxg#6F>ZZcgbh03!^pd z-lqu5evO^c?bs9BPHi9zyHX|jlej<pzVQB+_ovAQCzp3(J=rf!r$<m&Pkw+sPp~ui zAyige+3`p1{`TIk6K_MU!#l8=*7FRmM3(R*3$>ftrL~(<R+>JL{q|j)<8Z!EoSysb z&EKDsdGexIJ(p6)H>s1(zmH3I85pnb)z9w>^6FZQdU<i^j1v9F+@c>YmrHlR{!c5( zuEi()cE!Ny{vRHv>G<uz&&=iN(>c65U6<W&@GigJ`1*GD7rYZDXG)+fhk1uNp-bK= zdy}f*J?6c@TvPg*vCnxmP|kk-%{5!{@2|T)?<#}$?CU-D{eO7Q-0gbO^~==C*8KJE zH`h#i*=JqPfBXII?EB|T1G>v+So%%vJpOdwV)*q<i@PMy{gi$En-Z9}nd9ph-SXl! zrR(YUzvvi9ZVf)h|KRzF5GM>=qJ!eSO@u1pl3#;KUNqG466c>By!V)#`}O-KDvK$T zDVwQ-g<?>hOx>LS;pop<$uZ^Fa~x=-T|vKg97~Qh$DFRjIsfys**|zT=LgRue(-GW z51!5Y!L#{4cqaLSXA3w3en7j}51)$v@agOyKArQ!rxHJWI`@Z9=l$^M{2xA*{Nd9d z@oF~t%Cr<XoF42vO`d-FI1+r!tc|(XgEZ;4dp&4QC&IpQoSyb%zvCkriHQl)U4D*5 zZw~#6h;&`Gksa>E5fb`(MFsZe2nu#xO`)rw=sTt!0Zz&Q4*iOVe7zE2fE+^P#$%pU zP?>${yRIvXUes^dzQD{XEBA3Y{l1nW!bzdO8`b}7DRf0&4ES1#2*;mB{DEIf5#d<y zcg;>2TEA`WAkJ}q2`XKO`jR32Dsz_8nL3C5=r3KX)2|AhGe;qRJ9SS!;sHPX_8`i7 zQCojvplIyD&))xf@ih~qTKT@~r7F~0t*HO1Q=c{C*wA}Vi7$L45eZ7ne-fPa^vmJQ zCXVoN6zQGK99xbtz3)P;F{Ue1wsb9Os|aTim1arSz@ELglJhHl!>dDywG$?+g|H<{ zVM%3K|KYbH-}I}la(QJiepP{Q%G9AU?Wnfj%97yB;>@8mo052UyBYc%*mtd|%~s#E zoVm+buUV8kQ(}(U#792PQo1r{-)5+`C3+d#X3nvox0!KYku#Mj-aub5$0CwEAIYZc z9mWPS_3+xwTy=5E=67#=Tdy?f3m->|-msx^9XTdc(~o%k8=p8NCx$QIfBSpx%q1%4 zQJPOll4&&ck1kn@a~AxYvNqz3qOy4O{uNozBrcBQpUbbnzldL!e>VRT{ssIZM2RwI zCP#^1jIN|eZ~y-+_eGpR{~)^`%l%uPEg|mwYw}m4@A$~d7?Z~FB=?&oV04@eA@xN& zdc&G}i52DF_FE2r4*zgU(#H1orkmGWxrjJ#wz9UD8Ruwcz0lso!gRB-{mjjlCU!RV zHf9bpO>8zVG`8P7%Sn8k$Yx_}OEXh@2OU#8drKQ@nQ`K?#Ky_??<*oA?O^9<@1SmN zX7eXYo743jeQ9rM;%H~-;KG(8LWi`S=@v(-#dL#~ou!kdm8rR@efPs}A1gc4rw-JR zrKV1%Rw7o+ugo}Odv$9kn@y&6<3t=S6--P_?TI!sV=H^pakA2)-DQ9Oad-LOyzvjU zON)NjR%y|0qUm#KQ64{KwG^}zl$6w$sp{%}J^8=uzs?U%4j)IJ#x;vL+c@VquQ>m> z;JEO(n7EX<oVbEGZd`3#Q(RkIM_gx|Nc@y|+j!@AuXz9X;P~+PnE0Idf_QFxMSOL9 zZG2OFTYN`+XS`s7P=ZLplmxK^$prZXtpwc!qXdft+XUwXuY{O{l!Tmwf&^|tMM8B# zZ9-E*M?z-;Cs80#Fi|K`BymcjSfXU2e4<*SR-$gAQKChnZK89cSE7GnaAJ63Okzr6 zPGUhKH?bnII<YpfDX}flHpw~3E6G18I4L|SCMhK;C#fKbn^cihom89Dl+>2gk<^*Q zNft;JOcqKONuH7{mYkDPkRqR|ma3Jin`)G5k!qXjoa&VtoEn}Qlgdr4NUct-O>IiG zP4`OoPY+HHPmf7YNzX|yNav<kq*tfcrZ=UxrFW!vriW+5WTa%|WN^5AG|S;|niAR) zIMsYK0#b}KZc3bFoLZc2oCWdJpZJ+VysRKzwh<o%;)RHllJRQsy73nAMp?F5URl9e zF<CiT+^p)XrmT)EPPSmSNVZtEe707$QMPTiS9WlAOm<E-H@iB!DZ3+^(?s=<%s4TE zF(NTyG4e55F-9@AF<vpjF)=YYG2EEyn5LMH7*4EUtVpa_tbD9itWm6OtXFJsY)ouU zEH}0~wkftFmJ=r!ClV(XCm*L3XGHS%BH71~+_`bpB=wFsPP|~eNW56Qe7sh?5y{?* z<Q_v(=aR%@(o)iL(hAbJX%%VJX|-uhX>Dm8X`N}Dbb)libfI*S^eO3L>5}R4>1yd( z>ALAg=@#j>>CXR@ubd2l48aVc43Uf}8Dbfd8S)uw8Cn^-8Acfv8MYbD8D1Iw8NnGm z?iXZmGb%EwGio!MGTJgaGCDIjnF5)DnL?Q&nNu>wG9@$RGu1M+GIcYJGA%M~Go3TN zGW|1yGs81uGUc<?h`+kTVGH81GjZ9U_#950P9a_w5VtuUe4JkxHh1>|x)AHZQDb>8 z7Gpu&t|d;l5wAOm+XBRIN#b;HY;CL%aW$B};}HKOskc;6?-1(N`?l{i{~z_9LmJC) zmEk7C%fE+{{|PRpWaeZRWO6erGOIIdGn+EoGIg_@v%<3qvTC!0NQWg!i+Oym&aNf> zZ6git%;w1V;J6bzIWYpH#X_XVQ%I8~Nte|~n{`Q_El8uCNvHiutHX)YDWus2q}vsw z-M{C$5b61pSZ*>mr6Q#|r8cE0r7fi+r89+-Dv&CaCZ85VTH28&M7n58eVVEE|BnCT L!2fZ8!}<RJFEk9c literal 0 HcmV?d00001 diff --git a/tests/pe_files/message_table_resource.exe b/tests/pe_files/message_table_resource.exe new file mode 100644 index 0000000000000000000000000000000000000000..b28bd212a1a89e01445db69d7ef9ad94943b8cb4 GIT binary patch literal 6656 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 b9v6m%jBrpeiDqQ(7I%oZioGIKpW5~>uh0ul literal 0 HcmV?d00001 diff --git a/tests/pe_files/test_dll_32.dll b/tests/pe_files/test_dll_32.dll new file mode 100644 index 0000000000000000000000000000000000000000..5f9a485e00e147d79bd505020578dccac1f29e41 GIT binary patch literal 6656 zcmeHLeQ;A%7Qbl{2#`V;sURO~D6QL7DKE(jO=)2RG)q|thNiSsK0-)do0NRim-pD> zfZ~MC7@u859Y<&P53r-V<2t*D%u-hu0!1qft_<$hIx;&Qx4WC#>Q+%{hsxXEeM#CN z;(q<Jv+JFnbM8H#_nv$1y{~cJ^B7SRLNowbCZrRTHoJnq8I3?Z{np=1Cr78ewXjpQ z?yZHjfndzo!bh8UZ`kPbMj}zcxPvqDV#FAX7_07f8^cjQSDK$cwJ78I$OEsu@WprD z9nHc=@w-Mp!}z24+R@7j-8=fFLiZ?qxv^^WlA_-;no{_!8NGEG`a^}*1bqSO^F}Lh zIS5&&(vn{F0asS1kK`?!rkY9!{)j%!{op201Hh<^4N5<c5G_$Dj4UPFF+>uVz*`=% zLkJ^kH%`;T?Zt%r7~DGx34yAT=%Z;@bTrW*WWYaYBxL>s_qyH)<n~-H#`|-}U+I?$ zT&n=y?{q+PesryL^K+V!knN?s-z#_t`B)197-SM)Iv}Un5xO+3MmEtRVgSeunFYvc zc0!V+e2n*jr>qqWMek<NoMxx~crFx$;ts4`Nf7lnCr1fgJ0YvC<$OE(cQepz54>q4 zhQN8CW+E5t0qr_B(X#T3|3IF)f&8%Ql%7=NqcjX+-leXA1E>>8t_$qLgyn)3ST#t) zb#eiVUWdy9v<|543sGxH+P8JZZzHJ^lwjcV0%Ax_Mz32_G>!JJplBN`$!%Sdyipq3 zu)%Ff*439g^rE?3Ye2O77x@p-x{>qgNZO={b7MrZ79q}wYJ?d05JF3GAc;bY@71ej zw7ra;U&^OOQiCR~nsDe{iu&NpiY{SxAcg761trR2-Su)$TT=Y%;N)(Ho|fyOD)?>A z+t!v8rZnH0x&=pOusrZ2lmm|db!(}dDyBxTkcNh~<czlaWLXY8L{;_!rQV~0gq12< z-ft-1suy(#Q*b9_<qK-T73K#hNqt6_k3jjdXsDA#om@cKva<An0mS&m>5h$0H$IXl z<YTfS8hGF^3}q|b2Fc1Grrd)BV4C~mihyp7+#~f$4t>I5NNm-`2Q?L2_5AvH1raXE z1pzuuwJ;!CsNqPOkx<JUN77E^9@$ELP+5=U94U7iL~Xf4Cmc&S4GAZl>ZIPm7i3W{ z7lh!eA?-ma38gqJ$qt&Da>tmcb7k^^I`M4Vd<JFKYzVx$5V`3@ZlqD^Q)#OX!i1yW zawg#%OIUR%goI<P;uFzOu2hAnOE~)nj?rul3)=YAJhAl9YlvHMQqa}cx#jb)jqmLz zGxk3L9dJ^Qeksdf(v<AWu;!*&@vADa2<2d`cezuOz^iX2?vw`|#U-|C66MI}+rFTo z)Z%LD@B5I$NWCx}NJz(ra&b2P;5^AOB<Z9n31^>cL+UIFu%V$#;!<(0pU|4_c8py` z5v8VJK9Zw9B}1dDTT2%Uwwyj6w8BMgYCbZ9Vv(E%X`?=M4t5P&kvQcDJvdYX%MPOn zNy?v3alssl7tEsAYM^+z9=P$50-Hke?^MVFokFx4g%lHoEFMGrpFXN(Fp?akqe!8e zjuOyzhpvOA<F>vrS?<=hKvjkJ^bi$x6k*h@odY~YLz3Ov`HB>$i{2rLwqBSV(kR`F z=*k5KY*<GCjAm7;mu81vW3#{vB!T3DIl%GdRDp;(=p3aw0qR>Wm=BptE--?e=?YY$ zv{FBz;;!adfni!oVLU%yJrMoz5n7@2%-LbsFz9dArFKy>x*gd{N0p?DogRh0c1NG9 z-O=x&$+yzGPVRA~Dq#~pjjF#Wi~VwsQsJ`LhaNp7*Y4}Z)ORtJE<_^A1eGm=w^R*s zaJhG0@^WuO^;T?lU1ujIew?CuX{6$f*bHeh>M2h?AMe&wT;)IU&?~d9GfA~5x}lcx z2q<wRr>@g^4<U`^DnVmOBFxH4^~ncq^dn0Ao8`hQ({P_OmRHZ2@#HD#)YjbhBW;g! zU0tbubH=fiv)pwvj@>)Sa-qJxN;d~*VV<;CFIDR+M)@hyCY}1diYxroRq<p=MGv2{ zByqB@ad%Gf*L*c#D8vR2%q9l^A=Eu_#fNn6x_YHXOpX!D>&W)7zk`YrTneeAy#T<F zx*Z9NpVlBJwUR@(nDSg{iA#~_AXz5ai%zC)y?j32r^@Vu<Q(=V@~N8!OiKDQl0eU; z<-&vIxH^-{%IUGps9TcP8_30ey7R@`6}&~kn}8=?+jHmAYm%)rkW^J<NQ8<E?SMP_ zCYCpb<zZE_I3hX5GVySwj<2{YH*|Wk8`9T+EY8Avk*?kVXFv++p-P=Vu=wdYw0!WE z#)k|_9^>4Ia>2OkPptoo{nb}-66A_*A-}Clm~#1i2R(y>TwS|k1Sg1K8Im|=5pn6; z(6<cB1)2=iDHOAVo}TJCKC?K6EyER;#mUF-hUY;;+a*yiy^%k24IwA{4AX`aRywcC zJqas~vdy#kOxHQNxE988u}i`06kM&~Dg_I`G;80se<cH@#rPBld|E_EFW@xbdBDSf zU4R{cDnKRRHo(n*Nq{-9q3!pJ2ssBh4mb*U3~(PH0JsOR4qyi`fD*u5zz{%bS(+JA zA{egU#q9rRxBy86U|$HBLA5at)CYY(YPlHww}$vbAy1<i@tJb^=A6ETDxp=U$-jpY z?=Wso6|8IARqh9>I~($!mF<tJZ-2(V;=;$2zk1o0jWLdoZCMox#$sElqCPRqMTFRv zx?oK7hKz1O^arCxGh<@5xcF!@=YuT3g+fu!kD`3Yzomu?ao!lWMZm&5SXF6@e+T^H z%KO?zKLM!Ve1>Mn@7|QoX9wspK$d^*D|)}eH#N1-%6oI<6}V>C14as|M!HSM-t93G z@>&k>kKozC+mpkqrgZ||*R}tK+MAx|<YF;z6Spebx{_>kJ8CTEQly&@R_zIUu^&E< zj~9rfZR9zQkX!ORT*QYTG5ns02(`m`o?s*>2pk_KFHQ749C=;k@rGkf9<DWr$~37w zp4BZan|v+84}u}6lPfCf*yHDRh)qo#?+HYsI|*?^&xf6lc!b?89DFxMJrQr1i=hjv zh!-<!*LcJTCbpAohBhV}jVM7DsXYq!jM|goINf!tYfKE2UeQ9#z17FH2%dm9;tz2= z>+way;b??>NF2(dTu~(oIZLWIy2$K!C~qx|PQ7z}N2?F^O&mF*afdjrg$(3MS4YDw zUY;9QB1ugxk`s)01rCMoL$>i;s@8DAYLVx0B(ltRG#ex7dC>@%WiT4aq>4PJadX0r z#p<=Os!SSPJQssg@-y`Yk>hu}IKDB;hrJOWw>m1)HIOIr5W6<$+o>=J_Gw}+wE>>< z`q%pL4CQb$GX#4#wRP`~30$}~80J>SFhP#?$X$+_^^SGf+PSvcv-j5#oCPgJ^rsXx z;313;0$v2r(`N^~Py3l>#P}VIPXNvU^1xS-@e6@gDIjy*WqTIlj5XyJb8dZSJx@^j zvcGi8i7Fg-rA<h_?fpMxK%*>HrTo-}UqJR{*Pi9m+GD<kkN=~Tw{(*o?>!#A{<Hz9 z^u}Ubct>cru{9iu#FiI|d}LY77vRF)*y3=|$46t)Mqx4TpJm=yxOA7P(1=qSY{Y9< zhi4OKe|e#)lqp=1Kh<cg6nHTvti@gUPkOVY?O++>d?Ftdb}N32Xq7y7uZSp|-^B-a z;rKRjv2kIp*wG48dI_!Lc5xwNh~nji-q_m6uINsVFEon5l|Fi%EiY{JhGJadipnM9 zzOOYN_n)J2UG&N&*UYtY$@rpSUb!Tj&lRqfu9a0)YuB&Yv?<g1Psa4#p&v48M#mVK zMNA39Fsqqr#>F%-_cISM?abrM&zYmlADBNe!_4Q*JX4X$YI2%(n3_ysQ=92w(^IDB zO)r{WHk~z{H}#vwOtZ{$&5O-W^EPwPe6RT#^Fi}-<`>K_nU9;_HV>Ic%`+{2%K^)K zmYM8qb^*JH{T{o7UCJ(JtJu5P8n%Jm&IZ^Pww2w_wzH43PqWXmzhHmOo?^S%ciD^V z2W*PH%4)58>s)KG^$zO_tJ}KGDp((}K4pE!+G`!PUbS{E{q54XmwvQVRW`XSQYMt$ zSGKS0K-n+Lu9OkmY}+#18k^g;+2*rFZBN?{+FrCBv31&x+fLig+EC=i^ykWBOp-ax YuqK<S&y+F^nRMnNa|!*B{2yz72Te<1*Z=?k literal 0 HcmV?d00001 diff --git a/tests/pe_files/test_dll_64.dll b/tests/pe_files/test_dll_64.dll new file mode 100644 index 0000000000000000000000000000000000000000..2bbcfcc401474468ee0ec92c8d187da60c0887af GIT binary patch literal 7680 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 Yh34<5*z#n_j-DNf9Y9XB|EmoA8+$h|%>V!Z literal 0 HcmV?d00001 diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 0000000..c148e99 --- /dev/null +++ b/tests/test.h @@ -0,0 +1,124 @@ +#pragma once +#include <string> +#include <iostream> +#include <typeinfo> +#include <exception> +#include <stdlib.h> + +enum test_level +{ + test_level_normal, + test_level_critical +}; + +static void pe_test_print_error(const std::string& test_name, + test_level level, + const std::string& expression, + const std::string& file, size_t line) +{ + std::cerr << test_name << " - FAIL!" << std::endl + << "File: " << file << ", line: " << line << std::endl + << "Expression: " << expression << std::endl << std::endl; + + if(level == test_level_critical) + exit(EXIT_FAILURE); +} + +static void pe_test(bool result, + const std::string& test_name, test_level level, + const std::string& expression, + const std::string& file, size_t line) +{ + if(result) + std::cout << test_name << " - OK" << std::endl; + else + pe_test_print_error(test_name, level, expression, file, line); +} + +static void pe_test_error(const std::exception& e, + const std::string& test_name, test_level level, + const std::string& expression, + const std::string& file, size_t line) +{ + std::cerr << test_name << " FAIL!" << std::endl + << "File: " << file << ", line: " << line << std::endl + << "Expression: " << expression << std::endl + << "Exception occured: " << e.what() << std::endl + << "Exception type: " << typeid(e).name() << std::endl << std::endl; + + if(level == test_level_critical) + exit(EXIT_FAILURE); +} + +#define PE_TEST_TO_STRING(expression) #expression + +//Checks the result of expression (true) +#define PE_TEST(expression, test_name, level) \ + try \ + { \ + pe_test((expression), test_name, level, PE_TEST_TO_STRING(expression), __FILE__, __LINE__); \ + } \ + catch(const std::exception& e) \ + { \ + pe_test_error(e, test_name, level, PE_TEST_TO_STRING(expression), __FILE__, __LINE__); \ + } + +//Runs expression and checks for exceptions +#define PE_TEST_EXCEPTION(expression, test_name, level) \ + try \ + { \ + (expression); \ + std::cout << test_name << " - OK" << std::endl; \ + } \ + catch(const std::exception& e) \ + { \ + pe_test_error(e, test_name, level, PE_TEST_TO_STRING(expression), __FILE__, __LINE__); \ + } + +//Awaits exception from expression +#define PE_TEST_EXPECT_EXCEPTION(expression, pe_exception_code, test_name, level) \ + try \ + { \ + (expression); \ + std::cerr << "Expected exception: " << PE_TEST_TO_STRING(pe_exception_code) << std::endl; \ + pe_test_print_error(test_name, level, PE_TEST_TO_STRING(expression), __FILE__, __LINE__); \ + } \ + catch(const pe_exception& e) \ + { \ + if(e.get_id() == pe_exception_code) \ + std::cout << test_name << " - OK" << std::endl; \ + else \ + pe_test_error(e, test_name, level, PE_TEST_TO_STRING(expression), __FILE__, __LINE__); \ + } + + +#ifndef PE_FILES_UNUSED +static bool open_pe_file(int argc, char* argv[], std::auto_ptr<std::ifstream>& pe_file) +{ + if(argc != 2) + { + std::cerr << "Usage: test.exe PE_FILE" << std::endl; + return false; + } + + pe_file.reset(new std::ifstream(argv[1], std::ios::in | std::ios::binary)); + if(!*pe_file) + { + std::cerr << "Cannot open " << argv[1] << std::endl; + return false; + } + + return true; +} +#endif + +#define PE_TEST_START \ + try \ + { + + +#define PE_TEST_END } \ + catch(const std::exception& e) \ + { \ + pe_test_error(e, "Global Test", test_level_critical, "global test exception handler", __FILE__, __LINE__); \ + } diff --git a/tests/test_bound_import/Makefile b/tests/test_bound_import/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_bound_import/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_bound_import/main.cpp b/tests/test_bound_import/main.cpp new file mode 100644 index 0000000..24c3ce8 --- /dev/null +++ b/tests/test_bound_import/main.cpp @@ -0,0 +1,83 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +void test_bound_imports(const pe_base& image) +{ + bound_import_module_list imports; + PE_TEST_EXCEPTION(imports = get_bound_import_module_list(image), "Bound Import Parser test", test_level_critical); + PE_TEST(imports.size() == 2, "Bound Import test 1", test_level_critical); + PE_TEST(imports[0].get_module_name() == "USER32.dll" + && imports[1].get_module_name() == "KERNEL32.dll", "Bound Import test 2", test_level_normal); + + if(image.get_pe_type() == pe_type_32) + { + PE_TEST(imports[0].get_timestamp() == 0x4a5bdb3c + && imports[1].get_timestamp() == 0x4afc68c0, "Bound Import test 3", test_level_normal); + } + else + { + PE_TEST(imports[0].get_timestamp() == 0x4a5bdb3c + && imports[1].get_timestamp() == 0, "Bound Import test 3", test_level_normal); + } + + PE_TEST(imports[0].get_module_ref_count() == 0 + && imports[1].get_module_ref_count() == 1, "Bound Import test 4", test_level_critical); + PE_TEST(imports[1].get_module_ref_list()[0].get_module_name() == "NTDLL.DLL" + && imports[1].get_module_ref_list()[0].get_timestamp() == 0x4afc681b, "Bound Import test 5", test_level_normal); +} + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + test_bound_imports(image); + + { + std::stringstream new_pe(std::ios::in | std::ios::out | std::ios::binary); + PE_TEST_EXCEPTION(rebuild_pe(image, new_pe, false, true, true), "Bound Rebuild PE test 1", test_level_critical); + PE_TEST_EXCEPTION(image = pe_factory::create_pe(new_pe), "Bound Rebuild PE test 2", test_level_critical); + test_bound_imports(image); + + new_pe.str(""); + PE_TEST_EXCEPTION(rebuild_pe(image, new_pe, true, true, true), "Bound Rebuild PE test 3", test_level_critical); + PE_TEST_EXCEPTION(image = pe_factory::create_pe(new_pe), "Bound Rebuild PE test 4", test_level_critical); + test_bound_imports(image); + } + + + section s; + s.get_raw_data().resize(1); + s.set_name("newbound"); + s.readable(true); + section& new_bound_import_section = image.add_section(s); + bound_import_module_list imports; + PE_TEST_EXCEPTION(imports = get_bound_import_module_list(image), "Bound Import Parser test", test_level_critical); + + uint32_t old_bound_import_rva = image.get_directory_rva(pe_win::image_directory_entry_bound_import); + PE_TEST_EXCEPTION(rebuild_bound_imports(image, imports, new_bound_import_section, 0, true, true), "Bound Import Rebuilder test 1", test_level_critical); + PE_TEST(old_bound_import_rva != image.get_directory_rva(pe_win::image_directory_entry_bound_import), "Bound Import Directory test", test_level_normal); + test_bound_imports(image); + + new_bound_import_section.set_raw_data("111"); + old_bound_import_rva = image.get_directory_rva(pe_win::image_directory_entry_bound_import); + PE_TEST_EXCEPTION(rebuild_bound_imports(image, imports, new_bound_import_section, 3, true, true), "Bound Import Rebuilder test 2", test_level_critical); + PE_TEST(new_bound_import_section.get_raw_data().substr(0, 3) == "111", "Bound Import Rebuilder Offset test", test_level_normal); + PE_TEST(old_bound_import_rva != image.get_directory_rva(pe_win::image_directory_entry_bound_import), "Bound Import Directory test 2", test_level_normal); + test_bound_imports(image); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_bound_import/test_bound_import.vcproj b/tests/test_bound_import/test_bound_import.vcproj new file mode 100644 index 0000000..77eebb5 --- /dev/null +++ b/tests/test_bound_import/test_bound_import.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_bound_import" + ProjectGUID="{6EBEAFA6-7489-4026-83D1-CAF67D243119}" + RootNamespace="test_bound_import" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_bound_import/test_bound_import.vcxproj b/tests/test_bound_import/test_bound_import.vcxproj new file mode 100644 index 0000000..f6a44c4 --- /dev/null +++ b/tests/test_bound_import/test_bound_import.vcxproj @@ -0,0 +1,163 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{DA8A8F03-E719-45EF-A376-766A18772FA5}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_bound_import</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_bound_import/test_bound_import.vcxproj.filters b/tests/test_bound_import/test_bound_import.vcxproj.filters new file mode 100644 index 0000000..2a14256 --- /dev/null +++ b/tests/test_bound_import/test_bound_import.vcxproj.filters @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_checksum/Makefile b/tests/test_checksum/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_checksum/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_checksum/main.cpp b/tests/test_checksum/main.cpp new file mode 100644 index 0000000..c5dad08 --- /dev/null +++ b/tests/test_checksum/main.cpp @@ -0,0 +1,28 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + uint32_t checksum = -1; + PE_TEST_EXCEPTION(checksum = calculate_checksum(*pe_file), "Checksum test 1", test_level_normal); + PE_TEST(image.get_checksum() == checksum, "Checksum test 2", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_checksum/test_checksum.vcproj b/tests/test_checksum/test_checksum.vcproj new file mode 100644 index 0000000..b409b5a --- /dev/null +++ b/tests/test_checksum/test_checksum.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_checksum" + ProjectGUID="{7F95DC75-2CFA-4D0D-BD43-1BF6749F16EE}" + RootNamespace="test_checksum" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_checksum/test_checksum.vcxproj b/tests/test_checksum/test_checksum.vcxproj new file mode 100644 index 0000000..f6f2ae3 --- /dev/null +++ b/tests/test_checksum/test_checksum.vcxproj @@ -0,0 +1,168 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{7B7AEAB2-7755-409D-A6C9-D5FFB7D1A95A}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_other</RootNamespace> + <ProjectName>test_checksum</ProjectName> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_checksum/test_checksum.vcxproj.filters b/tests/test_checksum/test_checksum.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_checksum/test_checksum.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_debug/Makefile b/tests/test_debug/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_debug/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_debug/main.cpp b/tests/test_debug/main.cpp new file mode 100644 index 0000000..7b562d4 --- /dev/null +++ b/tests/test_debug/main.cpp @@ -0,0 +1,122 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +void debug_test(const debug_info& dbg) +{ + PE_TEST(dbg.get_characteristics() == 0 + && dbg.get_time_stamp() == 0x50757757 + && dbg.get_major_version() == 0 + && dbg.get_minor_version() == 0 + && dbg.get_rva_of_raw_data() == 0, + "Debug Basic Info test", test_level_normal); +} + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + debug_info_list info; + PE_TEST_EXCEPTION(info = get_debug_information(image), "Debug Info Parser test", test_level_critical); + + if(image.get_pe_type() == pe_type_32) + { + { + PE_TEST(info.size() == 3, "Debug Info test 1", test_level_critical); + debug_info& dbg = info[0]; + debug_test(dbg); + PE_TEST(dbg.get_size_of_data() == 0x00008CB1, "Debug Info test 2", test_level_normal); + + PE_TEST(dbg.get_type() == debug_info::debug_type_coff + && dbg.get_advanced_info_type() == debug_info::advanced_info_coff, "Debug Info test 3", test_level_critical); + + coff_debug_info advanced; + PE_TEST_EXCEPTION(advanced = dbg.get_advanced_debug_info<coff_debug_info>(), "Debug COFF Info Parser test", test_level_critical); + PE_TEST(advanced.get_number_of_line_numbers() == 0 + && advanced.get_number_of_symbols() == 0x4E9, "Debug Info test 4", test_level_critical); + PE_TEST(advanced.get_lva_to_first_line_number() == 0 + && advanced.get_lva_to_first_symbol() == 0x20 + && advanced.get_rva_to_first_byte_of_code() == 0x1000 + && advanced.get_rva_to_first_byte_of_data() == 0xE000 + && advanced.get_rva_to_last_byte_of_code() == 0xE000 + && advanced.get_rva_to_last_byte_of_data() == 0x7000, "Debug Info test 5", test_level_normal); + + const coff_debug_info::coff_symbol& sym = advanced.get_symbols()[1]; + PE_TEST(sym.get_index() == 0x55 && sym.get_rva() == 0xCD1C + && sym.get_section_number() == 1 + && sym.get_storage_class() == 3 + && sym.get_type() == 0 + && !sym.is_file() + && sym.get_symbol() == "UnwindUpVec", "Debug Info test 6", test_level_normal); + } + + { + debug_info& dbg = info[1]; + debug_test(dbg); + PE_TEST(dbg.get_size_of_data() == 0x110, "Debug Info test 7", test_level_normal); + + PE_TEST(dbg.get_type() == debug_info::debug_type_misc + && dbg.get_advanced_info_type() == debug_info::advanced_info_misc, "Debug Info test 8", test_level_critical); + + misc_debug_info advanced; + PE_TEST_EXCEPTION(advanced = dbg.get_advanced_debug_info<misc_debug_info>(), "Debug MISC Info Parser test", test_level_critical); + PE_TEST(advanced.is_exe_name(), "Debug MISC test 1", test_level_normal); + PE_TEST(!advanced.is_unicode(), "Debug MISC test 2", test_level_normal); + PE_TEST(advanced.get_data_ansi() == "Debug/debugtest.exe", "Debug MISC test 3", test_level_normal); + } + + { + debug_info& dbg = info[2]; + debug_test(dbg); + PE_TEST(dbg.get_size_of_data() == 0x68, "Debug Info test 9", test_level_normal); + + PE_TEST(dbg.get_type() == debug_info::debug_type_codeview + && dbg.get_advanced_info_type() == debug_info::advanced_info_pdb_2_0, "Debug Info test 10", test_level_critical); + + pdb_2_0_info advanced; + PE_TEST_EXCEPTION(advanced = dbg.get_advanced_debug_info<pdb_2_0_info>(), "Debug PDB 2.0 Info Parser test", test_level_critical); + PE_TEST(advanced.get_age() == 1, "Debug PDB 2.0 test 1", test_level_normal); + PE_TEST(advanced.get_signature() == 0x50757757, "Debug PDB 2.0 test 2", test_level_normal); + PE_TEST(advanced.get_pdb_file_name() == "C:\\Program Files (x86)\\Microsoft Visual Studio\\MyProjects\\debugtest\\Debug\\debugtest.pdb", "Debug PDB 2.0 test 3", test_level_normal); + } + } + else + { + PE_TEST(info.size() == 1, "Debug Info test 1", test_level_critical); + debug_info& dbg = info[0]; + PE_TEST(dbg.get_characteristics() == 0 + && dbg.get_time_stamp() == 0x50937d36 + && dbg.get_major_version() == 0 + && dbg.get_minor_version() == 0 + && dbg.get_rva_of_raw_data() == 0x0001F300 + && dbg.get_size_of_data() == 0x0000006F, + "Debug Basic Info test", test_level_normal); + + PE_TEST(dbg.get_type() == debug_info::debug_type_codeview + && dbg.get_advanced_info_type() == debug_info::advanced_info_pdb_7_0, "Debug Info test 2", test_level_critical); + + pdb_7_0_info advanced; + PE_TEST_EXCEPTION(advanced = dbg.get_advanced_debug_info<pdb_7_0_info>(), "Debug PDB 7.0 Info Parser test", test_level_critical); + PE_TEST(advanced.get_age() == 1, "Debug PDB 7.0 test 1", test_level_normal); + + pe_win::guid testguid = {0xCC311030, 0xD245, 0x4FF7, {0x9F, 0x16, 0xB5, 0x6D, 0x8B, 0x11, 0x1F, 0x1A}}; + PE_TEST(advanced.get_guid() == testguid, "Debug PDB 7.0 test 2", test_level_normal); + PE_TEST(advanced.get_pdb_file_name() == "C:\\Users\\Bliss\\Documents\\Visual Studio 2010\\Projects\\hello_world\\x64\\Release\\test1.pdb", "Debug PDB 7.0 test 3", test_level_normal); + } + + PE_TEST_END + + return 0; +} diff --git a/tests/test_debug/test_debug.vcproj b/tests/test_debug/test_debug.vcproj new file mode 100644 index 0000000..9f6220e --- /dev/null +++ b/tests/test_debug/test_debug.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_debug" + ProjectGUID="{42AC1521-0800-4D81-9363-6EF9362F7A4A}" + RootNamespace="test_debug" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_debug/test_debug.vcxproj b/tests/test_debug/test_debug.vcxproj new file mode 100644 index 0000000..3a3857b --- /dev/null +++ b/tests/test_debug/test_debug.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{B82FC407-B927-49D1-9DEB-0DFC3DC12A9C}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_debug</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_debug/test_debug.vcxproj.filters b/tests/test_debug/test_debug.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_debug/test_debug.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_dotnet/Makefile b/tests/test_dotnet/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_dotnet/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_dotnet/main.cpp b/tests/test_dotnet/main.cpp new file mode 100644 index 0000000..982e419 --- /dev/null +++ b/tests/test_dotnet/main.cpp @@ -0,0 +1,39 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + basic_dotnet_info info; + PE_TEST_EXCEPTION(info = get_basic_dotnet_info(image), "Basic Dotnet Info Parser test", test_level_critical); + PE_TEST(info.get_flags() == 1, "DotNet test 1", test_level_normal); + PE_TEST(info.get_major_runtime_version() == 2 && info.get_minor_runtime_version() == 5, "DotNet test 2", test_level_normal); + PE_TEST(info.get_rva_of_metadata() == 0x2064 && info.get_size_of_metadata() == 0x598, "DotNet test 3", test_level_normal); + PE_TEST(info.get_rva_of_resources() == 0 && info.get_size_of_resources() == 0, "DotNet test 4", test_level_normal); + PE_TEST(info.get_rva_of_strong_name_signature() == 0 && info.get_size_of_strong_name_signature() == 0, "DotNet test 5", test_level_normal); + PE_TEST(info.get_rva_of_code_manager_table() == 0 && info.get_size_of_code_manager_table() == 0, "DotNet test 6", test_level_normal); + PE_TEST(info.get_rva_of_vtable_fixups() == 0 && info.get_size_of_vtable_fixups() == 0, "DotNet test 7", test_level_normal); + PE_TEST(info.get_rva_of_export_address_table_jumps() == 0 && info.get_size_of_export_address_table_jumps() == 0, "DotNet test 8", test_level_normal); + PE_TEST(info.get_rva_of_managed_native_header() == 0 && info.get_size_of_managed_native_header() == 0, "DotNet test 9", test_level_normal); + PE_TEST(info.get_entry_point_rva_or_token() == 0x06000001, "DotNet test 10", test_level_normal); + PE_TEST(!info.is_native_entry_point(), "DotNet test 11", test_level_normal); + PE_TEST(!info.is_32bit_required(), "DotNet test 12", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_dotnet/test_dotnet.vcproj b/tests/test_dotnet/test_dotnet.vcproj new file mode 100644 index 0000000..e855bd0 --- /dev/null +++ b/tests/test_dotnet/test_dotnet.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_dotnet" + ProjectGUID="{094A7331-54E1-4034-BD1E-BE2F974B0142}" + RootNamespace="test_dotnet" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_dotnet/test_dotnet.vcxproj b/tests/test_dotnet/test_dotnet.vcxproj new file mode 100644 index 0000000..82474d1 --- /dev/null +++ b/tests/test_dotnet/test_dotnet.vcxproj @@ -0,0 +1,168 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{F8A9C956-AA19-4AEF-B1B7-E7C392E437FE}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_dotner</RootNamespace> + <ProjectName>test_dotnet</ProjectName> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_dotnet/test_dotnet.vcxproj.filters b/tests/test_dotnet/test_dotnet.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_dotnet/test_dotnet.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_entropy/Makefile b/tests/test_entropy/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_entropy/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_entropy/main.cpp b/tests/test_entropy/main.cpp new file mode 100644 index 0000000..8e40eca --- /dev/null +++ b/tests/test_entropy/main.cpp @@ -0,0 +1,34 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + PE_TEST(entropy_calculator::calculate_entropy(image) > 3.0, "Entropy test 1", test_level_normal); + PE_TEST(entropy_calculator::calculate_entropy(*pe_file) > 3.0, "Entropy test 2", test_level_normal); + PE_TEST(entropy_calculator::calculate_entropy(image.get_image_sections().at(0)) > 3.0, "Entropy test 3", test_level_normal); + + const char data[] = "123456789"; + PE_TEST(entropy_calculator::calculate_entropy(data, sizeof(data) - 1) > 3.0, "Entropy test 4", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(entropy_calculator::calculate_entropy("", 0), pe_exception::data_is_empty, "Entropy test 5", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(entropy_calculator::calculate_entropy(section()), pe_exception::section_is_empty, "Entropy test 6", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_entropy/test_entropy.vcproj b/tests/test_entropy/test_entropy.vcproj new file mode 100644 index 0000000..e823067 --- /dev/null +++ b/tests/test_entropy/test_entropy.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_entropy" + ProjectGUID="{D3FAD2A8-FF48-4E59-A347-C54AD9DB6AC4}" + RootNamespace="test_entropy" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_entropy/test_entropy.vcxproj b/tests/test_entropy/test_entropy.vcxproj new file mode 100644 index 0000000..756cfd0 --- /dev/null +++ b/tests/test_entropy/test_entropy.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{853CFFF4-1FAB-48EB-81A9-CC35F9FB3F80}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_entropy</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_entropy/test_entropy.vcxproj.filters b/tests/test_entropy/test_entropy.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_entropy/test_entropy.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_exception_directory/Makefile b/tests/test_exception_directory/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_exception_directory/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_exception_directory/main.cpp b/tests/test_exception_directory/main.cpp new file mode 100644 index 0000000..1105ba5 --- /dev/null +++ b/tests/test_exception_directory/main.cpp @@ -0,0 +1,40 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + if(image.get_pe_type() == pe_type_64) + { + exception_entry_list info; + PE_TEST_EXCEPTION(info = get_exception_directory_data(image), "Exception directory parser test", test_level_critical); + PE_TEST(info.size() == 0x1C6, "Exception directory test 1", test_level_normal); + PE_TEST(info[5].get_begin_address() == 0x000011D5 + && info[5].get_end_address() == 0x00001220, "Exception directory test 2", test_level_normal); + PE_TEST(info[5].get_flags() == 4, "Exception directory test 3", test_level_normal); + PE_TEST(info[5].get_unwind_info_address() == 0x21528, "Exception directory test 4", test_level_normal); + PE_TEST(info[5].get_unwind_info_version() == 1, "Exception directory test 5", test_level_normal); + PE_TEST(info[5].get_size_of_prolog() == 0x5, "Exception directory test 6", test_level_normal); + PE_TEST(info[5].get_number_of_unwind_slots() == 2, "Exception directory test 7", test_level_normal); + PE_TEST(info[5].get_frame_pointer_register_number() == 0, "Exception directory test 8", test_level_normal); + PE_TEST(info[5].get_scaled_rsp_offset() == 0, "Exception directory test 9", test_level_normal); + } + + PE_TEST_END + + return 0; +} diff --git a/tests/test_exception_directory/test_exception_directory.vcproj b/tests/test_exception_directory/test_exception_directory.vcproj new file mode 100644 index 0000000..4c3a39f --- /dev/null +++ b/tests/test_exception_directory/test_exception_directory.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_exception_directory" + ProjectGUID="{1C36ED94-CBE5-4107-83B6-9C37F3A4041C}" + RootNamespace="test_exception_directory" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_exception_directory/test_exception_directory.vcxproj b/tests/test_exception_directory/test_exception_directory.vcxproj new file mode 100644 index 0000000..a49a8a9 --- /dev/null +++ b/tests/test_exception_directory/test_exception_directory.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{B6A37BAA-484D-4175-BEA2-62892A12E8F5}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_exception_directory</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_exception_directory/test_exception_directory.vcxproj.filters b/tests/test_exception_directory/test_exception_directory.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_exception_directory/test_exception_directory.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_exports/Makefile b/tests/test_exports/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_exports/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_exports/main.cpp b/tests/test_exports/main.cpp new file mode 100644 index 0000000..019b245 --- /dev/null +++ b/tests/test_exports/main.cpp @@ -0,0 +1,147 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +void test_exports(const export_info& info, const exported_functions_list& exports, const pe_base& image, bool test_rvas = true) +{ + PE_TEST(info.get_characteristics() == 0 + && info.get_major_version() == 0 + && info.get_minor_version() == 0 + && info.get_ordinal_base() == 5 + && info.get_name() == "test_dll.dll" + && info.get_number_of_functions() == 6 + && info.get_number_of_names() == 3, "Exports test 1", test_level_normal); + + if(test_rvas) + { + if(image.get_pe_type() == pe_type_32) + { + PE_TEST(info.get_timestamp() == 0x509103D8 + && info.get_rva_of_functions() == 0x00002588 + && info.get_rva_of_names() == 0x000025A0 + && info.get_rva_of_name_ordinals() == 0x000025AC, "Exports test 2", test_level_normal); + } + else + { + PE_TEST(info.get_timestamp() == 0x509103D3 + && info.get_rva_of_functions() == 0x00002718 + && info.get_rva_of_names() == 0x00002730 + && info.get_rva_of_name_ordinals() == 0x0000273C, "Exports test 2", test_level_normal); + } + } + + PE_TEST(exports.size() == 4, "Exports test 3", test_level_critical); + + PE_TEST(exports[0].has_name() && exports[0].get_name() == "dll_func1", "Exports test 4", test_level_normal); + PE_TEST(!exports[0].is_forwarded(), "Exports test 5", test_level_normal); + PE_TEST(exports[0].get_name_ordinal() == 0 && exports[0].get_ordinal() == 5, "Exports test 6", test_level_normal); + PE_TEST(exports[0].get_rva() == 0x00001000, "Exports test 7", test_level_normal); + + PE_TEST(exports[2].has_name() && exports[2].get_name() == "MsgBoxA", "Exports test 8", test_level_normal); + PE_TEST(exports[2].is_forwarded() && exports[2].get_forwarded_name() == "USER32.MessageBoxA", "Exports test 9", test_level_normal); + PE_TEST(exports[2].get_name_ordinal() == 2 && exports[2].get_ordinal() == 7, "Exports test 10", test_level_normal); + + if(test_rvas) + { + if(image.get_pe_type() == pe_type_32) + { + PE_TEST(exports[2].get_rva() == 0x000025DB, "Exports test 11", test_level_normal); + } + else + { + PE_TEST(exports[2].get_rva() == 0x0000276B, "Exports test 11", test_level_normal); + } + } + + PE_TEST(!exports[3].has_name() && exports[3].get_ordinal() == 0xA, "Exports test 12", test_level_normal); + PE_TEST(!exports[3].is_forwarded(), "Exports test 13", test_level_normal); + PE_TEST(exports[3].get_rva() == 0x00001020, "Exports test 14", test_level_normal); + + std::pair<uint16_t, uint16_t> limits; + PE_TEST_EXCEPTION(limits = get_export_ordinal_limits(exports), "get_export_ordinal_limits test 1", test_level_normal); + PE_TEST(limits.first == info.get_ordinal_base() && limits.second == 0xA, "get_export_ordinal_limits test 2", test_level_normal); + + PE_TEST(exported_name_exists("MsgBoxA", exports), "exported_name_exists test 1", test_level_normal); + PE_TEST(exported_name_exists("dll_func1", exports), "exported_name_exists test 2", test_level_normal); + PE_TEST(!exported_name_exists("dll_func2", exports), "exported_name_exists test 3", test_level_normal); + PE_TEST(!exported_name_exists("USER32.MessageBoxA", exports), "exported_name_exists test 4", test_level_normal); + + PE_TEST(exported_ordinal_exists(0x5, exports), "exported_ordinal_exists test 1", test_level_normal); + PE_TEST(exported_ordinal_exists(0xA, exports), "exported_ordinal_exists test 2", test_level_normal); + PE_TEST(!exported_ordinal_exists(0x1, exports), "exported_ordinal_exists test 3", test_level_normal); + PE_TEST(!exported_ordinal_exists(0x9, exports), "exported_ordinal_exists test 4", test_level_normal); +} + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + exported_functions_list exports; + export_info info; + PE_TEST_EXCEPTION(exports = get_exported_functions(image, info), "Exports Parser test 1", test_level_critical); + test_exports(info, exports, image); + + PE_TEST_EXCEPTION(rebuild_exports(image, info, exports, image.section_from_directory(pe_win::image_directory_entry_export), 0, true, true), "Exports Rebuilder test 1", test_level_critical); + PE_TEST_EXCEPTION(exports = get_exported_functions(image, info), "Exports Parser test 2", test_level_critical); + test_exports(info, exports, image, false); + + section s; + s.get_raw_data().resize(1); + s.set_name("newexp"); + section& new_export_section = image.add_section(s); + uint32_t old_export_rva = image.get_directory_rva(pe_win::image_directory_entry_export); + PE_TEST_EXCEPTION(rebuild_exports(image, info, exports, new_export_section, 0, true, true), "Exports Rebuilder test 2", test_level_critical); + PE_TEST(old_export_rva != image.get_directory_rva(pe_win::image_directory_entry_export), "Exports Rebuilder test 3", test_level_normal); + PE_TEST_EXCEPTION(exports = get_exported_functions(image, info), "Exports Parser test 3", test_level_critical); + test_exports(info, exports, image, false); + + new_export_section.set_raw_data("111"); + PE_TEST_EXCEPTION(rebuild_exports(image, info, exports, new_export_section, 3, true, true), "Exports Rebuilder test 4", test_level_critical); + PE_TEST(new_export_section.get_raw_data().substr(0, 3) == "111", "Exports Rebuilder offset test 1", test_level_normal); + PE_TEST_EXCEPTION(exports = get_exported_functions(image, info), "Exports Parser test 4", test_level_critical); + test_exports(info, exports, image, false); + + new_export_section.set_raw_data("111111111111"); + PE_TEST_EXCEPTION(rebuild_exports(image, info, exports, new_export_section, 12, true, true), "Exports Rebuilder test 5", test_level_critical); + PE_TEST(new_export_section.get_raw_data().substr(0, 12) == "111111111111", "Exports Rebuilder offset test 2", test_level_normal); + PE_TEST_EXCEPTION(exports = get_exported_functions(image, info), "Exports Parser test 5", test_level_critical); + test_exports(info, exports, image, false); + + exported_function func; + func.set_ordinal(0xA); + func.set_name("DuplicatedOrdinal"); + func.set_rva(0x1000); + exports.push_back(func); + PE_TEST_EXPECT_EXCEPTION(rebuild_exports(image, info, exports, new_export_section, 12, true, true), pe_exception::duplicate_exported_function_ordinal, "Exports Rebuilder test 6", test_level_normal); + + exports.back().set_ordinal(0xC); + exports.back().set_name("MsgBoxA"); //Duplicate name + PE_TEST_EXPECT_EXCEPTION(rebuild_exports(image, info, exports, new_export_section, 12, true, true), pe_exception::duplicate_exported_function_name, "Exports Rebuilder test 7", test_level_normal); + + exports.back().set_ordinal(0xC); + exports.back().set_name("ANewFunction"); + exports.back().set_ordinal(0xF); + PE_TEST_EXCEPTION(rebuild_exports(image, info, exports, new_export_section, 12, true, true), "Exports Rebuilder test 8", test_level_normal); + PE_TEST_EXCEPTION(exports = get_exported_functions(image, info), "Exports Parser test 6", test_level_critical); + + PE_TEST(exports.size() == 5, "New Exported Function test 1", test_level_critical); + PE_TEST(exports[4].has_name() && exports[4].get_name() == "ANewFunction", "New Exported Function test 2", test_level_normal); + PE_TEST(!exports[4].is_forwarded(), "New Exported Function test 3", test_level_normal); + PE_TEST(exports[4].get_rva() == 0x00001000, "New Exported Function test 4", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_exports/test_exports.vcproj b/tests/test_exports/test_exports.vcproj new file mode 100644 index 0000000..8247e36 --- /dev/null +++ b/tests/test_exports/test_exports.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_exports" + ProjectGUID="{E126644C-38FF-41EF-9EAF-ED8C9FCF62EF}" + RootNamespace="test_exports" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_exports/test_exports.vcxproj b/tests/test_exports/test_exports.vcxproj new file mode 100644 index 0000000..016689d --- /dev/null +++ b/tests/test_exports/test_exports.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{82EAF17E-9618-4BD7-AE50-0C325591B585}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_exports</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_exports/test_exports.vcxproj.filters b/tests/test_exports/test_exports.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_exports/test_exports.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_imports/Makefile b/tests/test_imports/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_imports/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_imports/main.cpp b/tests/test_imports/main.cpp new file mode 100644 index 0000000..b897289 --- /dev/null +++ b/tests/test_imports/main.cpp @@ -0,0 +1,192 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +void compare_imports(const imported_functions_list& imports, const imported_functions_list& new_imports, bool compare_original_iat = true) +{ + PE_TEST(imports.size() == new_imports.size(), "Import compare test (libraries)", test_level_critical); + for(size_t i = 0; i != imports.size(); ++i) + { + std::cout << "Library iteration = " << i << std::endl; + const import_library::imported_list& funcs = imports[i].get_imported_functions(); + const import_library::imported_list& new_funcs = new_imports[i].get_imported_functions(); + + PE_TEST(imports[i].get_name() == new_imports[i].get_name() + && imports[i].get_rva_to_iat() == new_imports[i].get_rva_to_iat() + && imports[i].get_timestamp() == new_imports[i].get_timestamp(), + "Import compare test (library properties)", test_level_normal); + + if(compare_original_iat) + { + PE_TEST(imports[i].get_rva_to_original_iat() == new_imports[i].get_rva_to_original_iat(), "Import compare test (library properties)", test_level_normal); + } + + PE_TEST(funcs.size() == new_funcs.size(), "Import compare test (function count)", test_level_critical); + for(size_t j = 0; j != new_funcs.size(); ++j) + { + std::cout << "Function iteration = " << j << std::endl; + PE_TEST(funcs[i].has_name() == new_funcs[i].has_name(), "Import compare test (function properties)", test_level_normal); + + if(compare_original_iat) + { + PE_TEST(funcs[i].get_iat_va() == new_funcs[i].get_iat_va(), "Import compare test (function properties)", test_level_normal); + } + + if(funcs[i].has_name()) + { + PE_TEST(funcs[i].get_name() == new_funcs[i].get_name() && funcs[i].get_hint() == new_funcs[i].get_hint(), "Import compare test (function properties)", test_level_normal); + } + else + { + PE_TEST(funcs[i].get_ordinal() == new_funcs[i].get_ordinal(), "Import compare test (function properties)", test_level_normal); + } + } + } +} + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + imported_functions_list imports; + PE_TEST_EXCEPTION(imports = get_imported_functions(image), "get_imported_functions test", test_level_critical); + PE_TEST(imports.size() == 2, "Imports test 1", test_level_normal); + PE_TEST(imports.at(0).get_name() == "USER32.dll", "Imports test 2", test_level_normal); + PE_TEST(imports.at(1).get_name() == "KERNEL32.dll", "Imports test 3", test_level_normal); + + import_library& user32 = imports.at(0); + import_library& kernel32 = imports.at(1); + + PE_TEST(user32.get_imported_functions().at(0).has_name() && user32.get_imported_functions().at(0).get_name() == "MessageBoxW", "Imports test 4", test_level_normal); + PE_TEST(kernel32.get_timestamp() == 0, "Imports test 5", test_level_normal); + + if(image.get_pe_type() == pe_type_32) + { + PE_TEST(kernel32.get_rva_to_iat() == 0x00018000, "Imports test 6", test_level_normal); + PE_TEST(kernel32.get_rva_to_original_iat() == 0x0001CA20, "Imports test 7", test_level_normal); + PE_TEST(user32.get_imported_functions().at(0).get_hint() == 0x215, "Imports test 8", test_level_normal); + } + else + { + PE_TEST(kernel32.get_rva_to_iat() == 0x0001B000, "Imports test 6", test_level_normal); + PE_TEST(kernel32.get_rva_to_original_iat() == 0x00022428, "Imports test 7", test_level_normal); + PE_TEST(user32.get_imported_functions().at(0).get_hint() == 0x219, "Imports test 8", test_level_normal); + } + + + imported_functions_list new_imports; + + section s; + s.get_raw_data().resize(1); + section& import_section = image.add_section(s); + + import_rebuilder_settings settings(true, false); + settings.build_original_iat(true); + settings.enable_auto_strip_last_section(true); + settings.fill_missing_original_iats(false); + settings.save_iat_and_original_iat_rvas(true, false); + settings.set_offset_from_section_start(0); + + image_directory import_dir; + PE_TEST_EXCEPTION(import_dir = rebuild_imports(image, imports, import_section, settings), "Import rebuilder test 1", test_level_critical); + PE_TEST_EXCEPTION(new_imports = get_imported_functions(image), "get_imported_functions test 2", test_level_critical); + PE_TEST(import_dir.get_rva() == image.get_directory_rva(pe_win::image_directory_entry_import) + && import_dir.get_size() == image.get_directory_size(pe_win::image_directory_entry_import), "Import directory test 1", test_level_critical); + PE_TEST(image.get_directory_rva(pe_win::image_directory_entry_iat) && image.get_directory_size(pe_win::image_directory_entry_iat), "Import directory test 2", test_level_critical); + + compare_imports(imports, new_imports); + + settings.zero_directory_entry_iat(true); + settings.set_offset_from_section_start(1); + import_section.get_raw_data() = "x"; + PE_TEST_EXCEPTION(import_dir = rebuild_imports(image, imports, import_section, settings), "Import rebuilder test 2", test_level_critical); + PE_TEST_EXCEPTION(new_imports = get_imported_functions(image), "get_imported_functions test 3", test_level_critical); + PE_TEST(import_section.get_raw_data().substr(0, 1) == "x", "Import offset test 1", test_level_critical); + PE_TEST(!image.get_directory_rva(pe_win::image_directory_entry_iat) && !image.get_directory_size(pe_win::image_directory_entry_iat), "Import directory test 3", test_level_critical); + compare_imports(imports, new_imports); + + settings.set_offset_from_section_start(10); + import_section.get_raw_data() = "0123456789"; + PE_TEST_EXCEPTION(import_dir = rebuild_imports(image, imports, import_section, settings), "Import rebuilder test 3", test_level_critical); + PE_TEST_EXCEPTION(new_imports = get_imported_functions(image), "get_imported_functions test 4", test_level_critical); + PE_TEST(import_section.get_raw_data().substr(0, 10) == "0123456789", "Import offset test 2", test_level_critical); + compare_imports(imports, new_imports); + + settings.save_iat_and_original_iat_rvas(true, true); + PE_TEST_EXCEPTION(import_dir = rebuild_imports(image, imports, import_section, settings), "Import rebuilder test 4", test_level_critical); + PE_TEST_EXCEPTION(new_imports = get_imported_functions(image), "get_imported_functions test 5", test_level_critical); + compare_imports(imports, new_imports); + + settings.build_original_iat(false); + PE_TEST_EXCEPTION(import_dir = rebuild_imports(image, imports, import_section, settings), "Import rebuilder test 5", test_level_critical); + PE_TEST_EXCEPTION(new_imports = get_imported_functions(image), "get_imported_functions test 6", test_level_critical); + PE_TEST(!new_imports[0].get_rva_to_original_iat() && !new_imports[1].get_rva_to_original_iat(), "Import original IAT test", test_level_normal); + compare_imports(imports, new_imports, false); + + settings.build_original_iat(true); + PE_TEST_EXCEPTION(import_dir = rebuild_imports(image, imports, import_section, settings), "Import rebuilder test 6", test_level_critical); + PE_TEST_EXCEPTION(new_imports = get_imported_functions(image), "get_imported_functions test 7", test_level_critical); + PE_TEST(new_imports[0].get_rva_to_original_iat() && new_imports[1].get_rva_to_original_iat(), "Import original IAT test 2", test_level_normal); + compare_imports(imports, new_imports, false); + + settings.fill_missing_original_iats(true); + settings.build_original_iat(false); + PE_TEST_EXCEPTION(import_dir = rebuild_imports(image, imports, import_section, settings), "Import rebuilder test 7", test_level_critical); + PE_TEST_EXCEPTION(new_imports = get_imported_functions(image), "get_imported_functions test 8", test_level_critical); + compare_imports(imports, new_imports, false); + + settings.save_iat_and_original_iat_rvas(false); + PE_TEST_EXCEPTION(import_dir = rebuild_imports(image, imports, import_section, settings), "Import rebuilder test 8", test_level_critical); + PE_TEST_EXCEPTION(new_imports = get_imported_functions(image), "get_imported_functions test 9", test_level_critical); + PE_TEST(imports[0].get_rva_to_iat() != new_imports[0].get_rva_to_iat() + && imports[1].get_rva_to_iat() != new_imports[1].get_rva_to_iat(), "IAT rebuilder test", test_level_normal); + + + import_library lib; + lib.set_name("TEST.DLL"); + lib.set_timestamp(0x12345); + imported_function func; + func.set_name("TestFunc"); + func.set_iat_va(1); + lib.add_import(func); + func.set_name("AFunc"); + lib.add_import(func); + + func.set_name(""); + func.set_ordinal(123); + lib.add_import(func); + + func.set_name("BFunc"); + lib.add_import(func); + + imports.push_back(lib); + + import_rebuilder_settings new_settings; + new_settings.save_iat_and_original_iat_rvas(false); + + PE_TEST_EXCEPTION(import_dir = rebuild_imports(image, imports, import_section, new_settings), "Import rebuilder test 9", test_level_critical); + PE_TEST_EXCEPTION(new_imports = get_imported_functions(image), "get_imported_functions test 10", test_level_critical); + + PE_TEST(new_imports.size() == 3 && new_imports[2].get_name() == "TEST.DLL", "Added import test", test_level_normal); + PE_TEST(new_imports[2].get_imported_functions().size() == 4, "Added import function test 1", test_level_normal); + PE_TEST(new_imports[2].get_imported_functions()[1].get_name() == "AFunc", "Added import function test 2", test_level_normal); + PE_TEST(new_imports[2].get_imported_functions()[3].get_iat_va() == 1, "Added import function test 3", test_level_normal); + PE_TEST(new_imports[2].get_imported_functions()[2].has_name() == false, "Added import function test 4", test_level_normal); + PE_TEST(new_imports[2].get_imported_functions()[2].get_ordinal() == 123, "Added import function test 5", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_imports/test_imports.vcproj b/tests/test_imports/test_imports.vcproj new file mode 100644 index 0000000..3ea02ac --- /dev/null +++ b/tests/test_imports/test_imports.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_imports" + ProjectGUID="{BD969F96-E5A5-47B2-B5EF-B7999A441CE5}" + RootNamespace="test_imports" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_imports/test_imports.vcxproj b/tests/test_imports/test_imports.vcxproj new file mode 100644 index 0000000..3711c52 --- /dev/null +++ b/tests/test_imports/test_imports.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{CE1D0620-BC75-456F-914B-3BEBF5444B4C}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_imports</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_imports/test_imports.vcxproj.filters b/tests/test_imports/test_imports.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_imports/test_imports.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_load_config/Makefile b/tests/test_load_config/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_load_config/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_load_config/main.cpp b/tests/test_load_config/main.cpp new file mode 100644 index 0000000..7e03929 --- /dev/null +++ b/tests/test_load_config/main.cpp @@ -0,0 +1,83 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +void check_load_config(const image_config_info& info) +{ + PE_TEST(info.get_security_cookie_va() == 0x41E7F4, "Load Config Values test 1", test_level_normal); + PE_TEST(info.get_se_handler_count() == 0x20, "Load Config Values test 2", test_level_critical); + PE_TEST(info.get_time_stamp() == 0, "Load Config Values test 3", test_level_normal); + PE_TEST(info.get_se_handler_rvas()[1] == 0x731a, "Load Config Values test 4", test_level_normal); +} + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + if(image.get_pe_type() == pe_type_32) + { + image_config_info info; + PE_TEST_EXCEPTION(info = get_image_config(image), "Load Config Parser test 1", test_level_critical); + check_load_config(info); + + section s; + s.get_raw_data().resize(1); + s.set_name("newcfg"); + section& new_config_section = image.add_section(s); + + uint32_t old_dir_rva = image.get_directory_rva(pe_win::image_directory_entry_load_config); + + PE_TEST_EXCEPTION(rebuild_image_config(image, info, new_config_section, 0, false, false, true, true), "Load Config Rebuilder test 1", test_level_critical); + PE_TEST(old_dir_rva != image.get_directory_rva(pe_win::image_directory_entry_load_config), "Load Config test 5", test_level_normal); + + uint64_t old_se_handler_table_va = info.get_se_handler_table_va(); + PE_TEST_EXCEPTION(info = get_image_config(image), "Load Config Parser test 2", test_level_critical); + PE_TEST(old_se_handler_table_va == info.get_se_handler_table_va(), "Load Config test 5", test_level_normal); + check_load_config(info); + + PE_TEST_EXCEPTION(rebuild_image_config(image, info, new_config_section, 0, true, false, true, true), "Load Config Rebuilder test 2", test_level_critical); + PE_TEST_EXCEPTION(info = get_image_config(image), "Load Config Parser test 3", test_level_critical); + PE_TEST(old_se_handler_table_va != info.get_se_handler_table_va(), "Load Config test 6", test_level_normal); + check_load_config(info); + + info.add_lock_prefix_rva(0x123); + info.add_lock_prefix_rva(0x456); + info.add_lock_prefix_rva(0x789); + + PE_TEST_EXCEPTION(rebuild_image_config(image, info, new_config_section, 0, true, true, true, true), "Load Config Rebuilder test 3", test_level_critical); + PE_TEST_EXCEPTION(info = get_image_config(image), "Load Config Parser test 4", test_level_critical); + check_load_config(info); + PE_TEST(info.get_lock_prefix_rvas().size() == 3, "Load Config Lock Prefix test 1", test_level_normal); + PE_TEST(info.get_lock_prefix_rvas()[2] == 0x789, "Load Config Lock Prefix test 2", test_level_normal); + + PE_TEST_EXCEPTION(rebuild_image_config(image, info, new_config_section, 1, true, true, true, true), "Load Config Rebuilder test 4", test_level_critical); + PE_TEST_EXCEPTION(info = get_image_config(image), "Load Config Parser test 5", test_level_critical); + check_load_config(info); + PE_TEST_EXCEPTION(rebuild_image_config(image, info, new_config_section, 12, true, true, true, true), "Load Config Rebuilder test 5", test_level_critical); + PE_TEST_EXCEPTION(info = get_image_config(image), "Load Config Parser test 6", test_level_critical); + check_load_config(info); + + info.add_se_handler_rva(0x001); //Check sorting + info.set_lock_prefix_table_va(0); + PE_TEST_EXCEPTION(rebuild_image_config(image, info, new_config_section, 0, true, false, true, true), "Load Config Rebuilder test 5", test_level_critical); + PE_TEST_EXCEPTION(info = get_image_config(image), "Load Config Parser test 6", test_level_critical); + PE_TEST(info.get_se_handler_count() == 0x21, "Load Config Values test 5", test_level_critical); + PE_TEST(info.get_se_handler_rvas()[0] == 0x001, "Load Config Values test 6", test_level_normal); //Checks if list is sorted + } + + PE_TEST_END + + return 0; +} diff --git a/tests/test_load_config/test_load_config.vcproj b/tests/test_load_config/test_load_config.vcproj new file mode 100644 index 0000000..193a3e0 --- /dev/null +++ b/tests/test_load_config/test_load_config.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_load_config" + ProjectGUID="{089E9482-33DD-4C64-84A1-C9B5F10F802A}" + RootNamespace="test_load_config" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_load_config/test_load_config.vcxproj b/tests/test_load_config/test_load_config.vcxproj new file mode 100644 index 0000000..7c71dc4 --- /dev/null +++ b/tests/test_load_config/test_load_config.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{FAD361E1-1FD7-4993-BD20-7450026E51CC}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_load_config</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_load_config/test_load_config.vcxproj.filters b/tests/test_load_config/test_load_config.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_load_config/test_load_config.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_relocations/Makefile b/tests/test_relocations/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_relocations/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_relocations/main.cpp b/tests/test_relocations/main.cpp new file mode 100644 index 0000000..5a128ab --- /dev/null +++ b/tests/test_relocations/main.cpp @@ -0,0 +1,100 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +void test_relocations(const pe_base& image, const relocation_table_list& tables, bool read_absolute_entries) +{ + if(image.get_pe_type() == pe_type_32) + { + PE_TEST(tables.size() == 30, "Relocation test 1", test_level_critical); + PE_TEST(tables[1].get_rva() == 0x2000, "Relocation test 2", test_level_normal); + PE_TEST(tables[1].get_relocations().size() == (read_absolute_entries ? 22 : 21), "Relocation test 3", test_level_critical); + PE_TEST(tables[1].get_relocations()[1].get_rva() == 0x54, "Relocation test 4", test_level_normal); + PE_TEST(tables[1].get_relocations()[1].get_type() == pe_win::image_rel_based_highlow, "Relocation test 5", test_level_normal); + } + else + { + PE_TEST(tables.size() == 7, "Relocation test 1", test_level_critical); + PE_TEST(tables[1].get_rva() == 0x1C000, "Relocation test 2", test_level_normal); + PE_TEST(tables[4].get_relocations().size() == (read_absolute_entries ? 6 : 5), "Relocation test 3", test_level_critical); + PE_TEST(tables[1].get_relocations()[1].get_rva() == 0x4E8, "Relocation test 4", test_level_normal); + PE_TEST(tables[1].get_relocations()[1].get_type() == pe_win::image_rel_based_dir64, "Relocation test 5", test_level_normal); + } +} + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + relocation_table_list tables; + PE_TEST_EXCEPTION(tables = get_relocations(image, true), "Relocation parser test 1", test_level_critical); + test_relocations(image, tables, true); + + tables.clear(); + PE_TEST_EXCEPTION(tables = get_relocations(image, false), "Relocation parser test 2", test_level_critical); + test_relocations(image, tables, false); + + section& reloc_section = image.section_from_directory(pe_win::image_directory_entry_basereloc); + PE_TEST_EXCEPTION(rebuild_relocations(image, tables, reloc_section, 0, true, true), "Relocation Rebuilder test 1", test_level_critical); + PE_TEST_EXCEPTION(tables = get_relocations(image, true), "Relocation parser test 3", test_level_critical); + test_relocations(image, tables, true); + + PE_TEST_EXCEPTION(rebuild_relocations(image, tables, reloc_section, 0, true, true), "Relocation Rebuilder test 2", test_level_critical); + PE_TEST_EXCEPTION(tables = get_relocations(image, true), "Relocation parser test 4", test_level_critical); + test_relocations(image, tables, true); + + section s; + s.get_raw_data().resize(1); + s.set_name("newreloc"); + section& new_reloc_section = image.add_section(s); + + uint32_t old_reloc_rva = image.get_directory_rva(pe_win::image_directory_entry_basereloc); + + PE_TEST_EXCEPTION(rebuild_relocations(image, tables, new_reloc_section, 0, true, true), "Relocation Rebuilder test 3", test_level_critical); + PE_TEST(old_reloc_rva != image.get_directory_rva(pe_win::image_directory_entry_basereloc), "Relocation Rebuilder test 4", test_level_normal); + + old_reloc_rva = image.get_directory_rva(pe_win::image_directory_entry_basereloc); + + PE_TEST_EXCEPTION(tables = get_relocations(image, false), "Relocation parser test 5", test_level_critical); + test_relocations(image, tables, false); + + new_reloc_section.set_raw_data("111"); + PE_TEST_EXCEPTION(rebuild_relocations(image, tables, new_reloc_section, 3, true, true), "Relocation Rebuilder test 4", test_level_critical); + PE_TEST(new_reloc_section.get_raw_data().substr(0, 3) == "111", "Relocation Rebuilder offset test", test_level_normal); + PE_TEST(old_reloc_rva != image.get_directory_rva(pe_win::image_directory_entry_basereloc), "Relocation Rebuilder test 5", test_level_normal); + + PE_TEST_EXCEPTION(tables = get_relocations(image, false), "Relocation parser test 6", test_level_critical); + test_relocations(image, tables, false); + + relocation_table_list full_tables; //With absolute entries + PE_TEST_EXCEPTION(full_tables = get_relocations(image, true), "Relocation parser test 7", test_level_critical); + test_relocations(image, full_tables, true); + + pe_base old_image(image); + + PE_TEST_EXCEPTION(rebase_image(image, tables, image.get_image_base_64() + 0x10000), "PE Rebaser test 1", test_level_critical); + PE_TEST_EXCEPTION(rebase_image(image, full_tables, image.get_image_base_64() - 0x10000), "PE Rebaser test 2", test_level_critical); //Check that rebaser skips absolute entries + + uint16_t section_count = image.get_number_of_sections(); + for(uint16_t i = 0; i != section_count; ++i) + { + std::cout << "Rebaser control test iteration: " << i << std::endl; + PE_TEST(image.get_image_sections().at(i).get_raw_data() == old_image.get_image_sections().at(i).get_raw_data(), "Rebaser control test", test_level_normal); + } + + PE_TEST_END + + return 0; +} diff --git a/tests/test_relocations/test_relocations.vcproj b/tests/test_relocations/test_relocations.vcproj new file mode 100644 index 0000000..5d4456b --- /dev/null +++ b/tests/test_relocations/test_relocations.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_relocations" + ProjectGUID="{997A89F0-372D-4306-AE4D-7438D93273C3}" + RootNamespace="test_relocations" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_relocations/test_relocations.vcxproj b/tests/test_relocations/test_relocations.vcxproj new file mode 100644 index 0000000..7a62d8e --- /dev/null +++ b/tests/test_relocations/test_relocations.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{709B0E41-9792-4A0A-B28B-CBD06CE441B9}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_relocations</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_relocations/test_relocations.vcxproj.filters b/tests/test_relocations/test_relocations.vcxproj.filters new file mode 100644 index 0000000..2b7ea73 --- /dev/null +++ b/tests/test_relocations/test_relocations.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_bitmap/Makefile b/tests/test_resource_bitmap/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_resource_bitmap/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_resource_bitmap/main.cpp b/tests/test_resource_bitmap/main.cpp new file mode 100644 index 0000000..0555b78 --- /dev/null +++ b/tests/test_resource_bitmap/main.cpp @@ -0,0 +1,59 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + resource_directory root(get_resources(image)); + + pe_resource_manager res(root); + resource_bitmap_reader bmp_read(res); + resource_bitmap_writer bmp_write(res); + + PE_TEST_EXPECT_EXCEPTION(bmp_read.get_bitmap_by_name(L"TEST"), pe_exception::resource_directory_entry_not_found, "Bitmap Reader test 1", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(bmp_read.get_bitmap_by_name(123, L"TEST"), pe_exception::resource_directory_entry_not_found, "Bitmap Reader test 2", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(bmp_read.get_bitmap_by_id(123), pe_exception::resource_directory_entry_not_found, "Bitmap Reader test 3", test_level_normal); + + std::string bitmap; + PE_TEST_EXCEPTION(bitmap = bmp_read.get_bitmap_by_id(102), "Bitmap Reader test 4", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(bmp_read.get_bitmap_by_id(102, 1), pe_exception::resource_data_entry_not_found, "Bitmap Reader test 5", test_level_normal); + PE_TEST_EXCEPTION(bmp_write.add_bitmap(bitmap, L"TEST", 1049, 1234, 5678), "Bitmap Writer test 1", test_level_normal); + + std::string bitmap2; + PE_TEST_EXCEPTION(bitmap2 = bmp_read.get_bitmap_by_name(1049, L"TEST"), "Bitmap Reader test 6", test_level_critical); + PE_TEST(bitmap == bitmap2, "Bitmap Reader test 7", test_level_normal); + + PE_TEST_EXCEPTION(bmp_write.add_bitmap(bitmap, 9000, 1049, 1234, 5678), "Bitmap Writer test 2", test_level_critical); + PE_TEST_EXCEPTION(bitmap2 = bmp_read.get_bitmap_by_id(9000), "Bitmap Reader test 8", test_level_normal); + PE_TEST(bitmap == bitmap2, "Bitmap Reader test 9", test_level_normal); + + PE_TEST_EXCEPTION(bitmap = bmp_read.get_bitmap_by_id(103), "Bitmap Reader test 10", test_level_normal); + PE_TEST_EXCEPTION(bmp_write.add_bitmap(bitmap, 9000, 1049, 1234, 5678), "Bitmap Writer test 3 (bitmap replace test)", test_level_critical); + PE_TEST_EXCEPTION(bitmap2 = bmp_read.get_bitmap_by_id(9000), "Bitmap Reader test 11", test_level_normal); + PE_TEST(bitmap == bitmap2, "Bitmap Reader test 12", test_level_normal); + + PE_TEST_EXCEPTION(bmp_write.remove_bitmap(9000, 1049), "Bitmap Writer test 4", test_level_critical); + PE_TEST_EXPECT_EXCEPTION(bmp_read.get_bitmap_by_id(9000), pe_exception::resource_directory_entry_not_found, "Bitmap Reader test 13", test_level_normal); + + PE_TEST_EXCEPTION(bmp_write.remove_bitmap(L"TEST", 1049), "Bitmap Writer test 5", test_level_critical); + PE_TEST_EXPECT_EXCEPTION(bmp_read.get_bitmap_by_name(L"TEST"), pe_exception::resource_directory_entry_not_found, "Bitmap Reader test 14", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_resource_bitmap/test_resource_bitmap.vcproj b/tests/test_resource_bitmap/test_resource_bitmap.vcproj new file mode 100644 index 0000000..834ab60 --- /dev/null +++ b/tests/test_resource_bitmap/test_resource_bitmap.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_resource_bitmap" + ProjectGUID="{1B337DC2-628E-4DA4-8C0F-A6880289C6E2}" + RootNamespace="test_resource_bitmap" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_resource_bitmap/test_resource_bitmap.vcxproj b/tests/test_resource_bitmap/test_resource_bitmap.vcxproj new file mode 100644 index 0000000..cefdb85 --- /dev/null +++ b/tests/test_resource_bitmap/test_resource_bitmap.vcxproj @@ -0,0 +1,168 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{F401B9A2-B8CB-477A-A515-F029D0AA5553}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_bitmap_reader</RootNamespace> + <ProjectName>test_resource_bitmap</ProjectName> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_bitmap/test_resource_bitmap.vcxproj.filters b/tests/test_resource_bitmap/test_resource_bitmap.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_resource_bitmap/test_resource_bitmap.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_icon_cursor/Makefile b/tests/test_resource_icon_cursor/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_resource_icon_cursor/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_resource_icon_cursor/main.cpp b/tests/test_resource_icon_cursor/main.cpp new file mode 100644 index 0000000..25315c4 --- /dev/null +++ b/tests/test_resource_icon_cursor/main.cpp @@ -0,0 +1,120 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + resource_directory root(get_resources(image)); + + pe_resource_manager res(root); + resource_cursor_icon_reader ico_read(res); + resource_cursor_icon_writer ico_write(res); + + std::string icon, icon2; + + //Named icon groups tests + PE_TEST_EXCEPTION(icon = ico_read.get_single_icon_by_id(5), "Icon Reader test 1", test_level_normal); + PE_TEST_EXCEPTION(ico_write.add_icon(icon, L"NEW_GROUP", 1033, resource_cursor_icon_writer::icon_place_free_ids, 1234, 5678), "Icon Writer test 1", test_level_critical); + PE_TEST_EXCEPTION(icon2 = ico_read.get_icon_by_name(1033, L"NEW_GROUP"), "Icon Reader test 2", test_level_normal); //This group contains single icon + PE_TEST(icon == icon2, "Icon Reader test 3", test_level_normal); + PE_TEST_EXCEPTION(ico_read.get_single_icon_by_id(1), "Icon Reader test 4", test_level_normal); //icon_place_free_ids - the first free id was 1 + + PE_TEST_EXCEPTION(ico_write.remove_icon_group(L"NEW_GROUP", 1033), "Icon Writer test 2", test_level_critical); + PE_TEST_EXPECT_EXCEPTION(ico_read.get_icon_by_name(1033, L"NEW_GROUP"), pe_exception::resource_directory_entry_not_found, "Icon Reader test 5", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(ico_read.get_single_icon_by_id(1), pe_exception::resource_directory_entry_not_found, "Icon Reader test 6", test_level_normal); + + PE_TEST_EXCEPTION(icon = ico_read.get_icon_by_name(1049, L"MAIN_ICON"), "Icon Reader test 7", test_level_normal); + + PE_TEST_EXCEPTION(ico_write.add_icon(icon, L"NEW_GROUP", 1033, resource_cursor_icon_writer::icon_place_after_max_icon_id, 1234, 5678), "Icon Writer test 3", test_level_critical); + PE_TEST_EXCEPTION(icon2 = ico_read.get_icon_by_name(1033, L"NEW_GROUP"), "Icon Reader test 8", test_level_normal); //This group contains single icon + PE_TEST(icon == icon2, "Icon Reader test 9", test_level_normal); + PE_TEST_EXCEPTION(ico_read.get_single_icon_by_id(18), "Icon Reader test 10", test_level_normal); //icon_place_after_max_icon_id - the last free id was 17, and MAIN_ICON contains more than one icon + PE_TEST_EXCEPTION(ico_read.get_single_icon_by_id(19), "Icon Reader test 11", test_level_normal); + + PE_TEST_EXCEPTION(ico_write.remove_icon_group(L"NEW_GROUP", 1033), "Icon Writer test 4", test_level_critical); + + + //ID icon groups tests + PE_TEST_EXCEPTION(icon = ico_read.get_single_icon_by_id(5), "Icon Reader test 12", test_level_normal); + PE_TEST_EXCEPTION(ico_write.add_icon(icon, 777, 1033, resource_cursor_icon_writer::icon_place_free_ids, 1234, 5678), "Icon Writer test 5", test_level_critical); + PE_TEST_EXCEPTION(icon2 = ico_read.get_icon_by_id_lang(1033, 777), "Icon Reader test 13", test_level_normal); //This group contains single icon + PE_TEST(icon == icon2, "Icon Reader test 14", test_level_normal); + PE_TEST_EXCEPTION(ico_read.get_single_icon_by_id(1), "Icon Reader test 15", test_level_normal); //icon_place_free_ids - the first free id was 1 + + PE_TEST_EXCEPTION(ico_write.remove_icon_group(777, 1033), "Icon Writer test 6", test_level_critical); + PE_TEST_EXPECT_EXCEPTION(ico_read.get_icon_by_id_lang(1033, 777), pe_exception::resource_directory_entry_not_found, "Icon Reader test 16", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(ico_read.get_single_icon_by_id(1), pe_exception::resource_directory_entry_not_found, "Icon Reader test 17", test_level_normal); + + PE_TEST_EXCEPTION(icon = ico_read.get_icon_by_name(1049, L"MAIN_ICON"), "Icon Reader test 18", test_level_normal); + + PE_TEST_EXCEPTION(ico_write.add_icon(icon, 777, 1033, resource_cursor_icon_writer::icon_place_after_max_icon_id, 1234, 5678), "Icon Writer test 7", test_level_critical); + PE_TEST_EXCEPTION(icon2 = ico_read.get_icon_by_id_lang(1033, 777), "Icon Reader test 19", test_level_normal); //This group contains single icon + PE_TEST(icon == icon2, "Icon Reader test 20", test_level_normal); + PE_TEST_EXCEPTION(ico_read.get_single_icon_by_id(18), "Icon Reader test 21", test_level_normal); //icon_place_after_max_icon_id - the last free id was 17, and MAIN_ICON contains more than one icon + PE_TEST_EXCEPTION(ico_read.get_single_icon_by_id(19), "Icon Reader test 22", test_level_normal); + + PE_TEST_EXCEPTION(ico_write.remove_icon_group(777, 1033), "Icon Writer test 8", test_level_critical); + + + //Named cursor groups tests + PE_TEST_EXCEPTION(icon = ico_read.get_single_cursor_by_id(3), "Cursor Reader test 1", test_level_normal); + PE_TEST_EXCEPTION(ico_write.add_cursor(icon, L"NEW_GROUP", 1033, resource_cursor_icon_writer::icon_place_free_ids, 1234, 5678), "Cursor Writer test 1", test_level_critical); + PE_TEST_EXCEPTION(icon2 = ico_read.get_cursor_by_name(1033, L"NEW_GROUP"), "Cursor Reader test 2", test_level_normal); //This group contains single cursor + PE_TEST(icon == icon2, "Cursor Reader test 3", test_level_normal); + PE_TEST_EXCEPTION(ico_read.get_single_cursor_by_id(4), "Cursor Reader test 4", test_level_normal); //icon_place_free_ids - the first free id was 4 + + PE_TEST_EXCEPTION(ico_write.remove_cursor_group(L"NEW_GROUP", 1033), "Cursor Writer test 2", test_level_critical); + PE_TEST_EXPECT_EXCEPTION(ico_read.get_cursor_by_name(1033, L"NEW_GROUP"), pe_exception::resource_directory_entry_not_found, "Cursor Reader test 5", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(ico_read.get_single_cursor_by_id(4), pe_exception::resource_directory_entry_not_found, "Cursor Reader test 6", test_level_normal); + + PE_TEST_EXCEPTION(icon = ico_read.get_cursor_by_id_lang(1049, 105), "Cursor Reader test 7", test_level_normal); + + PE_TEST_EXCEPTION(ico_write.add_cursor(icon, L"NEW_GROUP", 1033, resource_cursor_icon_writer::icon_place_after_max_icon_id, 1234, 5678), "Cursor Writer test 3", test_level_critical); + PE_TEST_EXCEPTION(icon2 = ico_read.get_cursor_by_name(1033, L"NEW_GROUP"), "Cursor Reader test 8", test_level_normal); //This group contains single cursor + PE_TEST(icon == icon2, "Cursor Reader test 9", test_level_normal); + PE_TEST_EXCEPTION(ico_read.get_single_cursor_by_id(4), "Cursor Reader test 10", test_level_normal); //icon_place_after_max_icon_id - the last free id was 4, and cursor group "105" contains more than one cursor + PE_TEST_EXCEPTION(ico_read.get_single_cursor_by_id(5), "Cursor Reader test 11", test_level_normal); + + PE_TEST_EXCEPTION(ico_write.remove_cursor_group(L"NEW_GROUP", 1033), "Cursor Writer test 4", test_level_critical); + + + //ID cursor groups tests + PE_TEST_EXCEPTION(icon = ico_read.get_single_cursor_by_id(3), "Cursor Reader test 12", test_level_normal); + PE_TEST_EXCEPTION(ico_write.add_cursor(icon, 777, 1033, resource_cursor_icon_writer::icon_place_free_ids, 1234, 5678), "Cursor Writer test 5", test_level_critical); + PE_TEST_EXCEPTION(icon2 = ico_read.get_cursor_by_id_lang(1033, 777), "Cursor Reader test 13", test_level_normal); //This group contains single cursor + PE_TEST(icon == icon2, "Cursor Reader test 14", test_level_normal); + PE_TEST_EXCEPTION(ico_read.get_single_cursor_by_id(4), "Cursor Reader test 15", test_level_normal); //icon_place_free_ids - the first free id was 4 + + PE_TEST_EXCEPTION(ico_write.remove_cursor_group(777, 1033), "Cursor Writer test 6", test_level_critical); + PE_TEST_EXPECT_EXCEPTION(ico_read.get_cursor_by_id_lang(1033, 777), pe_exception::resource_directory_entry_not_found, "Cursor Reader test 16", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(ico_read.get_single_cursor_by_id(4), pe_exception::resource_directory_entry_not_found, "Cursor Reader test 17", test_level_normal); + + PE_TEST_EXCEPTION(icon = ico_read.get_cursor_by_id_lang(1049, 105), "Cursor Reader test 18", test_level_normal); + + PE_TEST_EXCEPTION(ico_write.add_cursor(icon, 777, 1033, resource_cursor_icon_writer::icon_place_after_max_icon_id, 1234, 5678), "Cursor Writer test 7", test_level_critical); + PE_TEST_EXCEPTION(icon2 = ico_read.get_cursor_by_id_lang(1033, 777), "Cursor Reader test 19", test_level_normal); //This group contains single cursor + PE_TEST(icon == icon2, "Cursor Reader test 20", test_level_normal); + PE_TEST_EXCEPTION(ico_read.get_single_cursor_by_id(4), "Cursor Reader test 21", test_level_normal); //icon_place_after_max_icon_id - the last free id was 4, and cursor group "105" contains more than one cursor + PE_TEST_EXCEPTION(ico_read.get_single_cursor_by_id(5), "Cursor Reader test 22", test_level_normal); + + PE_TEST_EXCEPTION(ico_write.remove_cursor_group(777, 1033), "Cursor Writer test 8", test_level_critical); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_resource_icon_cursor/test_resource_icon_cursor.vcproj b/tests/test_resource_icon_cursor/test_resource_icon_cursor.vcproj new file mode 100644 index 0000000..48ce2c4 --- /dev/null +++ b/tests/test_resource_icon_cursor/test_resource_icon_cursor.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_resource_icon_cursor" + ProjectGUID="{1E59538C-2C78-4D35-8639-568890543A4A}" + RootNamespace="test_resource_icon_cursor" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_resource_icon_cursor/test_resource_icon_cursor.vcxproj b/tests/test_resource_icon_cursor/test_resource_icon_cursor.vcxproj new file mode 100644 index 0000000..4d43453 --- /dev/null +++ b/tests/test_resource_icon_cursor/test_resource_icon_cursor.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{D9AC6F2E-3FE9-4D64-BEAA-C7104A0397B2}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_resource_icon_cursor</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_icon_cursor/test_resource_icon_cursor.vcxproj.filters b/tests/test_resource_icon_cursor/test_resource_icon_cursor.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_resource_icon_cursor/test_resource_icon_cursor.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_manager/Makefile b/tests/test_resource_manager/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_resource_manager/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_resource_manager/main.cpp b/tests/test_resource_manager/main.cpp new file mode 100644 index 0000000..48d7235 --- /dev/null +++ b/tests/test_resource_manager/main.cpp @@ -0,0 +1,68 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + resource_directory root(get_resources(image)); + + pe_resource_manager res(root); + + PE_TEST(res.remove_resource_type(pe_resource_viewer::resource_bitmap) == true, "Resource Manager test 1", test_level_normal); + PE_TEST(res.remove_resource_type(pe_resource_viewer::resource_bitmap) == false, "Resource Manager test 2", test_level_normal); + PE_TEST(res.remove_resource(L"DOESNOT_EXIST") == false, "Resource Manager test 3", test_level_normal); + + PE_TEST(res.remove_resource(pe_resource_viewer::resource_icon_group, 107) == true, "Resource Manager test 4", test_level_normal); + PE_TEST(res.remove_resource(pe_resource_viewer::resource_icon_group, 107) == false, "Resource Manager test 5", test_level_normal); + PE_TEST(res.remove_resource(pe_resource_viewer::resource_icon_group, L"MAIN_ICON") == true, "Resource Manager test 6", test_level_normal); + PE_TEST(res.remove_resource(pe_resource_viewer::resource_icon_group, L"MAIN_ICON") == false, "Resource Manager test 7", test_level_normal); + PE_TEST(res.remove_resource(pe_resource_viewer::resource_bitmap, 101) == false, "Resource Manager test 8", test_level_normal); + PE_TEST(res.remove_resource(pe_resource_viewer::resource_bitmap, L"TEST") == false, "Resource Manager test 9", test_level_normal); + PE_TEST(res.remove_resource(L"TEST", 1) == false, "Resource Manager test 10", test_level_normal); + PE_TEST(res.remove_resource(L"TEST", L"TEST") == false, "Resource Manager test 11", test_level_normal); + + PE_TEST(res.remove_resource(pe_resource_viewer::resource_cursor_group, 104, 1047) == false, "Resource Manager test 12", test_level_normal); + PE_TEST(res.remove_resource(pe_resource_viewer::resource_cursor_group, 104, 1049) == true, "Resource Manager test 13", test_level_normal); + PE_TEST(res.remove_resource(pe_resource_viewer::resource_cursor_group, 104, 1049) == false, "Resource Manager test 14", test_level_normal); + PE_TEST(res.remove_resource(L"TEST", 100, 1049) == false, "Resource Manager test 15", test_level_normal); + PE_TEST(res.remove_resource(L"TEST", L"TEST", 1049) == false, "Resource Manager test 16", test_level_normal); + PE_TEST(res.remove_resource(pe_resource_viewer::resource_cursor_group, L"TEST", 1049) == false, "Resource Manager test 17", test_level_normal); + + PE_TEST_EXCEPTION(res.add_resource("res data", pe_resource_viewer::resource_rcdata, L"TESTNAME", 1049, 123, 12345), "Resource Manager test 18", test_level_normal); + PE_TEST(res.get_resource_data_by_name(1049, pe_resource_viewer::resource_rcdata, L"TESTNAME").get_data() == "res data", "Resource Manager test 19", test_level_normal); + PE_TEST(res.get_resource_data_by_name(1049, pe_resource_viewer::resource_rcdata, L"TESTNAME").get_codepage() == 123, "Resource Manager test 20", test_level_normal); + + PE_TEST_EXCEPTION(res.add_resource("res data 2", L"ROOT", L"TESTNAME", 1049, 456, 12345), "Resource Manager test 21", test_level_normal); + PE_TEST(res.get_resource_data_by_name(1049, L"ROOT", L"TESTNAME").get_data() == "res data 2", "Resource Manager test 22", test_level_normal); + PE_TEST(res.get_resource_data_by_name(1049, L"ROOT", L"TESTNAME").get_codepage() == 456, "Resource Manager test 23", test_level_normal); + + PE_TEST_EXCEPTION(res.add_resource("res data", pe_resource_viewer::resource_rcdata, 12345, 1049, 123, 12345), "Resource Manager test 24", test_level_normal); + PE_TEST(res.get_resource_data_by_id(1049, pe_resource_viewer::resource_rcdata, 12345).get_data() == "res data", "Resource Manager test 25", test_level_normal); + PE_TEST(res.get_resource_data_by_id(1049, pe_resource_viewer::resource_rcdata, 12345).get_codepage() == 123, "Resource Manager test 26", test_level_normal); + + PE_TEST_EXCEPTION(res.add_resource("res data 2", L"ROOT", 12345, 1049, 456, 12345), "Resource Manager test 27", test_level_normal); + PE_TEST(res.get_resource_data_by_id(1049, L"ROOT", 12345).get_data() == "res data 2", "Resource Manager test 28", test_level_normal); + PE_TEST(res.get_resource_data_by_id(1049, L"ROOT", 12345).get_codepage() == 456, "Resource Manager test 29", test_level_normal); + + PE_TEST_EXCEPTION(res.add_resource("res data 3", L"ROOT", 12345, 1049, 456, 12345), "Resource Manager test 30", test_level_normal); + PE_TEST(res.get_resource_data_by_id(1049, L"ROOT", 12345).get_data() == "res data 3", "Resource Manager test 31", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_resource_manager/test_resource_manager.vcproj b/tests/test_resource_manager/test_resource_manager.vcproj new file mode 100644 index 0000000..e03bf43 --- /dev/null +++ b/tests/test_resource_manager/test_resource_manager.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_resource_manager" + ProjectGUID="{39E1826B-5436-47D3-9B95-D3C667691461}" + RootNamespace="test_resource_manager" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_resource_manager/test_resource_manager.vcxproj b/tests/test_resource_manager/test_resource_manager.vcxproj new file mode 100644 index 0000000..4f11ddb --- /dev/null +++ b/tests/test_resource_manager/test_resource_manager.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{415A9FD5-59F6-4B1B-8EB8-EBD87E37EEA4}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_resource_manager</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_manager/test_resource_manager.vcxproj.filters b/tests/test_resource_manager/test_resource_manager.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_resource_manager/test_resource_manager.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_message_table/Makefile b/tests/test_resource_message_table/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_resource_message_table/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_resource_message_table/main.cpp b/tests/test_resource_message_table/main.cpp new file mode 100644 index 0000000..d666f64 --- /dev/null +++ b/tests/test_resource_message_table/main.cpp @@ -0,0 +1,62 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + resource_directory root(get_resources(image)); + + pe_resource_manager res(root); + resource_message_list_reader msg(res); + + resource_message_list messages; + + //Unicode tests + PE_TEST_EXCEPTION(messages = msg.get_message_table_by_id_lang(1049, 1), "Message Table Parser test 1", test_level_critical); + PE_TEST(messages.size() == 2, "Message Table Parser test 2", test_level_critical); + PE_TEST(messages.find(0x01000000) != messages.end() + && messages.find(0xC1000001) != messages.end(), "Message Table Parser test 3", test_level_critical); + PE_TEST(messages[0xC1000001].is_unicode(), "Message Table Parser test 4", test_level_normal); + PE_TEST(messages[0xC1000001].get_unicode_string() == L"Ошибка!\r\n", "Message Table Parser test 5", test_level_normal); + + PE_TEST_EXCEPTION(messages = msg.get_message_table_by_id_lang(1033, 1), "Message Table Parser test 6", test_level_critical); + PE_TEST(messages.size() == 2, "Message Table Parser test 7", test_level_critical); + PE_TEST(messages.find(0x01000000) != messages.end() + && messages.find(0xC1000001) != messages.end(), "Message Table Parser test 8", test_level_critical); + PE_TEST(messages[0xC1000001].is_unicode(), "Message Table Parser test 9", test_level_normal); + PE_TEST(messages[0xC1000001].get_unicode_string() == L"Error!\r\n", "Message Table Parser test 10", test_level_normal); + + //ANSI Tests + PE_TEST_EXCEPTION(messages = msg.get_message_table_by_id_lang(1049, 2), "Message Table Parser test 11", test_level_critical); + PE_TEST(messages.size() == 2, "Message Table Parser test 12", test_level_critical); + PE_TEST(messages.find(0x01000000) != messages.end() + && messages.find(0xC1000001) != messages.end(), "Message Table Parser test 13", test_level_critical); + PE_TEST(!messages[0xC1000001].is_unicode(), "Message Table Parser test 14", test_level_normal); + PE_TEST(messages[0xC1000001].get_ansi_string() == "\xCE\xF8\xE8\xE1\xEA\xE0!\r\n", "Message Table Parser test 15", test_level_normal); //"Ошибка!\r\n" + + PE_TEST_EXCEPTION(messages = msg.get_message_table_by_id_lang(1033, 2), "Message Table Parser test 16", test_level_critical); + PE_TEST(messages.size() == 2, "Message Table Parser test 17", test_level_critical); + PE_TEST(messages.find(0x01000000) != messages.end() + && messages.find(0xC1000001) != messages.end(), "Message Table Parser test 18", test_level_critical); + PE_TEST(!messages[0xC1000001].is_unicode(), "Message Table Parser test 19", test_level_normal); + PE_TEST(messages[0xC1000001].get_ansi_string() == "Error!\r\n", "Message Table Parser test 20", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_resource_message_table/test_resource_message_table.vcproj b/tests/test_resource_message_table/test_resource_message_table.vcproj new file mode 100644 index 0000000..9203d50 --- /dev/null +++ b/tests/test_resource_message_table/test_resource_message_table.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_resource_message_table" + ProjectGUID="{57EFEFC9-E2D9-418E-9F05-3FD0D9921251}" + RootNamespace="test_resource_message_table" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_resource_message_table/test_resource_message_table.vcxproj b/tests/test_resource_message_table/test_resource_message_table.vcxproj new file mode 100644 index 0000000..f227613 --- /dev/null +++ b/tests/test_resource_message_table/test_resource_message_table.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{6CBACE55-8DDC-4EAE-A23A-DF412265D30C}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_resource_message_table</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_message_table/test_resource_message_table.vcxproj.filters b/tests/test_resource_message_table/test_resource_message_table.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_resource_message_table/test_resource_message_table.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_string_table/Makefile b/tests/test_resource_string_table/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_resource_string_table/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_resource_string_table/main.cpp b/tests/test_resource_string_table/main.cpp new file mode 100644 index 0000000..5e632d3 --- /dev/null +++ b/tests/test_resource_string_table/main.cpp @@ -0,0 +1,41 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + resource_directory root(get_resources(image)); + + pe_resource_manager res(root); + resource_string_table_reader str(res); + + resource_string_list strings; + PE_TEST_EXCEPTION(strings = str.get_string_table_by_id_lang(1049, 7), "String List Parser test 1", test_level_critical); + PE_TEST(strings.size() == 4, "String List Parser test 2", test_level_critical); + PE_TEST(strings.find(111) != strings.end(), "String List Parser test 3", test_level_critical); + PE_TEST(strings[111] == L"Test String 4", "String List Parser test 4", test_level_normal); + + std::wstring str_111; + PE_TEST_EXCEPTION(str_111 = str.get_string_by_id(111), "String List Parser test 5", test_level_normal); + PE_TEST(str_111 == L"Test String 4", "String List Parser test 6", test_level_normal); + PE_TEST(str_111 == str.get_string_by_id_lang(1049, 111), "String List Parser test 7", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_resource_string_table/test_resource_string_table.vcproj b/tests/test_resource_string_table/test_resource_string_table.vcproj new file mode 100644 index 0000000..d5f6737 --- /dev/null +++ b/tests/test_resource_string_table/test_resource_string_table.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_resource_string_table" + ProjectGUID="{C68A466D-0C1B-40BC-9AB1-49B582958524}" + RootNamespace="test_resource_string_table" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_resource_string_table/test_resource_string_table.vcxproj b/tests/test_resource_string_table/test_resource_string_table.vcxproj new file mode 100644 index 0000000..b09cde3 --- /dev/null +++ b/tests/test_resource_string_table/test_resource_string_table.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{5E32A144-2F2D-4BB1-BBEF-13BE94414E99}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_resource_string_table</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_string_table/test_resource_string_table.vcxproj.filters b/tests/test_resource_string_table/test_resource_string_table.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_resource_string_table/test_resource_string_table.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_version_info/Makefile b/tests/test_resource_version_info/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_resource_version_info/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_resource_version_info/main.cpp b/tests/test_resource_version_info/main.cpp new file mode 100644 index 0000000..b025ddb --- /dev/null +++ b/tests/test_resource_version_info/main.cpp @@ -0,0 +1,97 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +void test_version(const resource_version_info_reader& ver_reader, file_version_info& file_info, lang_string_values_map& strings, translation_values_map& translations) +{ + strings.clear(); + translations.clear(); + + PE_TEST_EXCEPTION(file_info = ver_reader.get_version_info(strings, translations), "Version Info Parser test 1", test_level_critical); + PE_TEST(strings.size() == 2 && translations.size() == 2, "Version Info Parser test 2", test_level_critical); + PE_TEST(strings.find(L"040004b0") != strings.end() && strings.find(L"041904b0") != strings.end(), "Version Info Parser test 3", test_level_critical); + PE_TEST(translations.find(0x0400) != translations.end() && translations.find(0x0419) != translations.end(), "Version Info Parser test 4", test_level_critical); + PE_TEST(strings[L"040004b0"][L"FileDescription"] == L"PE Bliss Test PE File", "Version Info Parser test 5", test_level_normal); + PE_TEST(strings[L"041904b0"][L"FileDescription"] == L"PE Bliss - ТеÑтовый PE-файл", "Version Info Parser test 6", test_level_normal); + PE_TEST((*translations.find(0x0400)).second == 0x4b0 && (*translations.find(0x0419)).second == 0x4b0, "Version Info Parser test 7", test_level_normal); + PE_TEST(file_info.get_file_date_ls() == 0 && file_info.get_file_date_ms() == 0 + && file_info.get_file_flags() == 0 && file_info.get_file_os() == file_version_info::file_os_nt_win32 + && file_info.get_file_subtype() == 0 && file_info.get_file_type() == file_version_info::file_type_application + && file_info.get_file_version_ls() == 0x00020001 && file_info.get_file_version_ms() == 0x00040003 + && file_info.get_product_version_ls() == 0x00070008 && file_info.get_product_version_ms() == 0x00050006 + && file_info.get_file_version_string<char>() == "4.3.2.1" + && file_info.get_product_version_string<wchar_t>() == L"5.6.7.8", "File Version Info Parser test", test_level_normal); + + version_info_viewer ver_view(strings, translations); + version_info_editor ver_edit(strings, translations); + + PE_TEST(version_info_viewer::translation_from_string(L"041904b0").first == 0x0419 + && version_info_viewer::translation_from_string(L"041904b0").second == 0x04b0, "translation_from_string test", test_level_normal); + + PE_TEST(ver_view.get_company_name() == L"PE Bliss", "Version Info Viewer test 1", test_level_normal); + PE_TEST(ver_view.get_company_name(L"040004b0") == L"PE Bliss", "Version Info Viewer test 2", test_level_normal); + PE_TEST(ver_view.get_file_description() == L"PE Bliss - ТеÑтовый PE-файл", "Version Info Viewer test 3", test_level_normal); + PE_TEST(ver_view.get_file_description(L"040004b0") == L"PE Bliss Test PE File", "Version Info Viewer test 4", test_level_normal); + PE_TEST(ver_view.get_file_version() == L"4.3.2.1", "Version Info Viewer test 5", test_level_normal); + PE_TEST(ver_view.get_file_version(L"040004b0") == L"4.3.2.1", "Version Info Viewer test 6", test_level_normal); + PE_TEST(ver_view.get_internal_name() == L"test.exe", "Version Info Viewer test 7", test_level_normal); + PE_TEST(ver_view.get_internal_name(L"040004b0") == L"test.exe", "Version Info Viewer test 8", test_level_normal); + PE_TEST(ver_view.get_legal_copyright() == L"(C) dx", "Version Info Viewer test 9", test_level_normal); + PE_TEST(ver_view.get_legal_copyright(L"040004b0") == L"(C) dx", "Version Info Viewer test 10", test_level_normal); + PE_TEST(ver_view.get_original_filename() == L"original.exe", "Version Info Viewer test 11", test_level_normal); + PE_TEST(ver_view.get_original_filename(L"040004b0") == L"original.exe", "Version Info Viewer test 12", test_level_normal); + PE_TEST(ver_view.get_product_name() == L"PE Bliss - ТеÑÑ‚Ñ‹", "Version Info Viewer test 13", test_level_normal); + PE_TEST(ver_view.get_product_name(L"040004b0") == L"PE Bliss Test", "Version Info Viewer test 14", test_level_normal); + PE_TEST(ver_view.get_product_version() == L"5.6.7.8", "Version Info Viewer test 15", test_level_normal); + PE_TEST(ver_view.get_product_version(L"040004b0") == L"5.6.7.8", "Version Info Viewer test 16", test_level_normal); + PE_TEST(ver_view.get_property(L"CompanyName", L"", false) == L"PE Bliss", "Version Info Viewer test 17", test_level_normal); + PE_TEST(ver_view.get_property(L"CompanyName", L"040004b0", false) == L"PE Bliss", "Version Info Viewer test 18", test_level_normal); + PE_TEST(ver_view.get_property(L"TestProperty", L"", false) == L"", "Version Info Viewer test 19", test_level_normal); + PE_TEST(ver_view.get_property(L"TestProperty", L"040004b0", false) == L"", "Version Info Viewer test 20", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(ver_view.get_property(L"TestProperty", L"", true) == L"", pe_exception::version_info_string_does_not_exist, "Version Info Viewer test 21", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(ver_view.get_property(L"TestProperty", L"040004b0", true) == L"", pe_exception::version_info_string_does_not_exist, "Version Info Viewer test 22", test_level_normal); + PE_TEST(ver_view.get_translation_list().size() == 2, "Version Info Viewer test 23", test_level_critical); + PE_TEST(ver_view.get_translation_list().at(1) == L"041904b0", "Version Info Viewer test 24", test_level_critical); +} + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + resource_directory root(get_resources(image)); + + pe_resource_manager res(root); + resource_version_info_reader ver_reader(res); + resource_version_info_writer ver_writer(res); + + file_version_info file_info; + lang_string_values_map strings; + translation_values_map translations; + test_version(ver_reader, file_info, strings, translations); + + PE_TEST(ver_writer.remove_version_info(1049) == true, "Version Info Writer test 1", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(ver_reader.get_version_info(strings, translations), pe_exception::resource_directory_entry_not_found, "Version Info Parser test", test_level_critical); + + PE_TEST_EXCEPTION(ver_writer.set_version_info(file_info, strings, translations, 12345, 678, 123), "Version Info Writer test 2", test_level_critical); + test_version(ver_reader, file_info, strings, translations); + + + + + PE_TEST_END + + return 0; +} diff --git a/tests/test_resource_version_info/test_resource_version_info.vcproj b/tests/test_resource_version_info/test_resource_version_info.vcproj new file mode 100644 index 0000000..c4fc107 --- /dev/null +++ b/tests/test_resource_version_info/test_resource_version_info.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_resource_version_info" + ProjectGUID="{C30B270A-4C93-44A3-AABE-633713D0F1D7}" + RootNamespace="test_resource_version_info" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_resource_version_info/test_resource_version_info.vcxproj b/tests/test_resource_version_info/test_resource_version_info.vcxproj new file mode 100644 index 0000000..5631f20 --- /dev/null +++ b/tests/test_resource_version_info/test_resource_version_info.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{5C2B081E-5414-437B-86EB-B2695AEDF3F0}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_resource_version_info</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_version_info/test_resource_version_info.vcxproj.filters b/tests/test_resource_version_info/test_resource_version_info.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_resource_version_info/test_resource_version_info.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_viewer/Makefile b/tests/test_resource_viewer/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_resource_viewer/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_resource_viewer/main.cpp b/tests/test_resource_viewer/main.cpp new file mode 100644 index 0000000..6a9812a --- /dev/null +++ b/tests/test_resource_viewer/main.cpp @@ -0,0 +1,89 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + resource_directory root(get_resources(image)); + + pe_resource_viewer res(root); + + PE_TEST_EXPECT_EXCEPTION(res.get_resource_count(L"NoName") == 0, pe_exception::resource_directory_entry_not_found, "Resource viewer test 1", test_level_normal); + PE_TEST(res.get_resource_count(pe_resource_viewer::resource_cursor) == 3, "Resource viewer test 2", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(res.get_language_count(L"NoName", 123) == 0, pe_exception::resource_directory_entry_not_found, "Resource viewer test 3", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_language_count(pe_resource_viewer::resource_accelerator, 123), pe_exception::resource_directory_entry_not_found, "Resource viewer test 4", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(res.get_language_count(pe_resource_viewer::resource_cursor, 5) == 0, pe_exception::resource_directory_entry_not_found, "Resource viewer test 5", test_level_normal); + PE_TEST(res.get_language_count(pe_resource_viewer::resource_cursor, 2) == 1, "Resource viewer test 6", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(res.get_language_count(pe_resource_viewer::resource_cursor, 5) == 0, pe_exception::resource_directory_entry_not_found, "Resource viewer test 7", test_level_normal); + PE_TEST(res.get_language_count(pe_resource_viewer::resource_cursor, 2) == 1, "Resource viewer test 8", test_level_normal); + + PE_TEST(res.get_language_count(pe_resource_viewer::resource_icon_group, L"MAIN_ICON") == 1, "Resource viewer test 9", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_language_count(pe_resource_viewer::resource_icon_group, L"DOESNT_EXIST") == 1, pe_exception::resource_directory_entry_not_found, "Resource viewer test 10", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(res.get_language_count(L"NONAME", L"DOESNT_EXIST") == 1, pe_exception::resource_directory_entry_not_found, "Resource viewer test 11", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_language_count(L"NONAME", 123) == 1, pe_exception::resource_directory_entry_not_found, "Resource viewer test 12", test_level_normal); + + PE_TEST(!res.resource_exists(L"NOT_EXISTENT"), "Resource viewer test 13", test_level_normal); + PE_TEST(res.resource_exists(pe_resource_viewer::resource_bitmap), "Resource viewer test 14", test_level_normal); + + PE_TEST(res.list_resource_types().size() == 8, "Resource viewer test 15", test_level_critical); + PE_TEST(res.list_resource_types()[7] == pe_resource_viewer::resource_manifest, "Resource viewer test 16", test_level_normal); + + PE_TEST(res.list_resource_names(pe_resource_viewer::resource_bitmap).size() == 0, "Resource viewer test 17", test_level_critical); + PE_TEST(res.list_resource_ids(pe_resource_viewer::resource_bitmap).size() == 3, "Resource viewer test 18", test_level_critical); + + PE_TEST_EXPECT_EXCEPTION(res.list_resource_names(L"DOESNOT_EXIST"), pe_exception::resource_directory_entry_not_found, "Resource viewer test 19", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.list_resource_ids(L"DOESNOT_EXIST"), pe_exception::resource_directory_entry_not_found, "Resource viewer test 20", test_level_normal); + + PE_TEST(res.list_resource_ids(pe_resource_viewer::resource_bitmap).at(2) == 103, "Resource viewer test 21", test_level_normal); + PE_TEST(res.list_resource_names(pe_resource_viewer::resource_icon_group).size() == 1, "Resource viewer test 22", test_level_critical); + PE_TEST(res.list_resource_names(pe_resource_viewer::resource_icon_group).at(0) == L"MAIN_ICON", "Resource viewer test 23", test_level_normal); + + PE_TEST(res.list_resource_languages(pe_resource_viewer::resource_icon_group, 107).size() == 1, "Resource viewer test 24", test_level_critical); + PE_TEST(res.list_resource_languages(pe_resource_viewer::resource_icon_group, 107).at(0) == 1049, "Resource viewer test 25", test_level_normal); + + PE_TEST(res.list_resource_languages(pe_resource_viewer::resource_icon_group, L"MAIN_ICON").size() == 1, "Resource viewer test 26", test_level_critical); + PE_TEST(res.list_resource_languages(pe_resource_viewer::resource_icon_group, L"MAIN_ICON").at(0) == 1049, "Resource viewer test 27", test_level_critical); + + PE_TEST_EXPECT_EXCEPTION(res.list_resource_languages(L"UNEXISTENT", L"MAIN_ICON"), pe_exception::resource_directory_entry_not_found, "Resource viewer test 28", test_level_critical); + PE_TEST_EXPECT_EXCEPTION(res.list_resource_languages(L"UNEXISTENT", 123), pe_exception::resource_directory_entry_not_found, "Resource viewer test 29", test_level_critical); + + PE_TEST(res.get_resource_data_by_id(pe_resource_viewer::resource_manifest, 1).get_codepage() == 0x4E4, "Resource viewer test 30", test_level_normal); + PE_TEST(res.get_resource_data_by_id(pe_resource_viewer::resource_manifest, 1).get_data().substr(0, 15) == "<assembly xmlns", "Resource viewer test 31", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_resource_data_by_id(pe_resource_viewer::resource_manifest, 1, 1), pe_exception::resource_data_entry_not_found, "Resource viewer test 32", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_resource_data_by_id(L"NONAME", 1), pe_exception::resource_directory_entry_not_found, "Resource viewer test 33", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_resource_data_by_id(1049, L"NONAME", 123), pe_exception::resource_directory_entry_not_found, "Resource viewer test 34", test_level_normal); + PE_TEST(res.get_resource_data_by_id(1033, pe_resource_viewer::resource_manifest, 1).get_codepage() == 0x4E4, "Resource viewer test 35", test_level_normal); + + PE_TEST(res.get_resource_data_by_name(pe_resource_viewer::resource_icon_group, L"MAIN_ICON").get_codepage() == 0x4E4, "Resource viewer test 36", test_level_normal); + PE_TEST(res.get_resource_data_by_name(pe_resource_viewer::resource_icon_group, L"MAIN_ICON").get_data().substr(0, 5) == std::string("\0\0\1\0\x0d", 5), "Resource viewer test 37", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_resource_data_by_name(pe_resource_viewer::resource_icon_group, L"MAIN_ICON", 1), pe_exception::resource_data_entry_not_found, "Resource viewer test 38", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_resource_data_by_name(L"NONAME", L"NONAME2"), pe_exception::resource_directory_entry_not_found, "Resource viewer test 39", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_resource_data_by_name(1049, L"QWERTY", L"QWERTY"), pe_exception::resource_directory_entry_not_found, "Resource viewer test 40", test_level_normal); + PE_TEST(res.get_resource_data_by_name(1049, pe_resource_viewer::resource_icon_group, L"MAIN_ICON").get_codepage() == 0x4E4, "Resource viewer test 41", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(res.get_resource_data_by_id(1032, pe_resource_viewer::resource_manifest, 1), pe_exception::resource_directory_entry_not_found, "Resource viewer test 42", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(res.get_resource_data_by_name(1050, pe_resource_viewer::resource_icon_group, L"MAIN_ICON"), pe_exception::resource_directory_entry_not_found, "Resource viewer test 43", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_resource_viewer/test_resource_viewer.vcproj b/tests/test_resource_viewer/test_resource_viewer.vcproj new file mode 100644 index 0000000..c163c4b --- /dev/null +++ b/tests/test_resource_viewer/test_resource_viewer.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_resource_viewer" + ProjectGUID="{DD6C58C3-6DD5-43B2-A9ED-760E5F5830AA}" + RootNamespace="test_resource_viewer" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_resource_viewer/test_resource_viewer.vcxproj b/tests/test_resource_viewer/test_resource_viewer.vcxproj new file mode 100644 index 0000000..ead35b4 --- /dev/null +++ b/tests/test_resource_viewer/test_resource_viewer.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{1FC3537C-EC13-4877-A06C-42DD8B81CBF3}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_resource_viewer</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resource_viewer/test_resource_viewer.vcxproj.filters b/tests/test_resource_viewer/test_resource_viewer.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_resource_viewer/test_resource_viewer.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resources/Makefile b/tests/test_resources/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_resources/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_resources/main.cpp b/tests/test_resources/main.cpp new file mode 100644 index 0000000..821f9ac --- /dev/null +++ b/tests/test_resources/main.cpp @@ -0,0 +1,89 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include <pe_bliss_resources.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +void test_resources(const resource_directory& root) +{ + PE_TEST(root.get_entry_list().size() == 8, "Resource test 1", test_level_critical); + PE_TEST(root.get_characteristics() == 0 && root.get_timestamp() == 0, "Resource test 2", test_level_normal); + PE_TEST(root.get_minor_version() == 0 && root.get_major_version() == 4, "Resource test 3", test_level_normal); + PE_TEST(root.get_number_of_named_entries() == 0 && root.get_number_of_id_entries() == 8, "Resource test 4", test_level_normal); + PE_TEST(!root.get_entry_list()[1].is_named() && root.get_entry_list()[1].get_id() == pe_resource_viewer::resource_bitmap, "Resource test 5", test_level_normal); + PE_TEST(!root.get_entry_list()[1].includes_data(), "Resource test 6", test_level_critical); + + const resource_directory& bitmap_root = root.get_entry_list()[1].get_resource_directory(); + PE_TEST(bitmap_root.get_number_of_named_entries() == 0 && bitmap_root.get_number_of_id_entries() == 3, "Resource test 7", test_level_critical); + PE_TEST(!bitmap_root.get_entry_list()[1].is_named() && bitmap_root.get_entry_list()[1].get_id() == 102, "Resource test 8", test_level_normal); + PE_TEST(!bitmap_root.get_entry_list()[1].includes_data(), "Resource test 9", test_level_critical); + + const resource_directory& bitmap_102_root = bitmap_root.get_entry_list()[1].get_resource_directory(); + PE_TEST(bitmap_102_root.get_number_of_named_entries() == 0 && bitmap_102_root.get_number_of_id_entries() == 1, "Resource test 10", test_level_critical); + PE_TEST(!bitmap_102_root.get_entry_list()[0].is_named() && bitmap_102_root.get_entry_list()[0].get_id() == 1049, "Resource test 11", test_level_normal); + PE_TEST(bitmap_102_root.get_entry_list()[0].includes_data(), "Resource test 12", test_level_critical); + + const resource_data_entry& bitmap_data = bitmap_102_root.get_entry_list()[0].get_data_entry(); + PE_TEST(bitmap_data.get_codepage() == 0x4E4, "Resource test 13", test_level_normal); + PE_TEST(bitmap_data.get_data().substr(0, 5) == std::string("\x28\0\0\0\x4f", 5) && bitmap_data.get_data().size() == 0x4EE8, "Resource test 14", test_level_normal); +} + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + resource_directory root; + + PE_TEST_EXCEPTION(root = get_resources(image), "Resource Directory Parser test 1", test_level_critical); + test_resources(root); + + section s; + s.get_raw_data().resize(1); + s.set_name("newrsrc"); + section& new_resource_section = image.add_section(s); + uint32_t old_resources_rva = image.get_directory_rva(pe_win::image_directory_entry_resource); + PE_TEST_EXCEPTION(rebuild_resources(image, root, new_resource_section, 0, true, true), "Resource Rebuilder test 1", test_level_critical); + PE_TEST_EXCEPTION(root = get_resources(image), "Resource Directory Parser test 2", test_level_critical); + PE_TEST(old_resources_rva != image.get_directory_rva(pe_win::image_directory_entry_resource), "Relocation Directory test", test_level_normal); + test_resources(root); + + new_resource_section.set_raw_data("111"); + PE_TEST_EXCEPTION(rebuild_resources(image, root, new_resource_section, 3, true, true), "Resource Rebuilder test 2", test_level_critical); + PE_TEST(new_resource_section.get_raw_data().substr(0, 3) == "111", "Resource Rebuilder Offset test", test_level_normal); + PE_TEST_EXCEPTION(root = get_resources(image), "Resource Directory Parser test 3", test_level_critical); + test_resources(root); + + PE_TEST_EXCEPTION(rebuild_resources(image, root, new_resource_section, 12, true, true), "Resource Rebuilder test 3", test_level_critical); + PE_TEST_EXCEPTION(root = get_resources(image), "Resource Directory Parser test 4", test_level_critical); + test_resources(root); + + { + resource_directory& cursor_root = root.get_entry_list()[0].get_resource_directory(); + resource_directory_entry named_entry; + named_entry.set_name(L"test entry"); + named_entry.add_data_entry(resource_data_entry("alala", 123)); + cursor_root.add_resource_directory_entry(named_entry); + } + + PE_TEST_EXCEPTION(rebuild_resources(image, root, new_resource_section, 12, true, true), "Resource Rebuilder test 4", test_level_critical); + PE_TEST_EXCEPTION(root = get_resources(image), "Resource Directory Parser test 5", test_level_critical); + test_resources(root); + + resource_directory& cursor_root = root.get_entry_list()[0].get_resource_directory(); + PE_TEST(cursor_root.entry_by_name(L"test entry").get_data_entry().get_data() == "alala", "Resource named entry test", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_resources/test_resources.vcproj b/tests/test_resources/test_resources.vcproj new file mode 100644 index 0000000..147df29 --- /dev/null +++ b/tests/test_resources/test_resources.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_resources" + ProjectGUID="{7E3867A9-59BC-4441-A74E-F4ABFFEE231C}" + RootNamespace="test_resources" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_resources/test_resources.vcxproj b/tests/test_resources/test_resources.vcxproj new file mode 100644 index 0000000..4d17f93 --- /dev/null +++ b/tests/test_resources/test_resources.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{8ECEF4F9-1461-4FCB-87D9-C871C71B01B7}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_resources</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_resources/test_resources.vcxproj.filters b/tests/test_resources/test_resources.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_resources/test_resources.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_rich_data/Makefile b/tests/test_rich_data/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_rich_data/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_rich_data/main.cpp b/tests/test_rich_data/main.cpp new file mode 100644 index 0000000..c631fa1 --- /dev/null +++ b/tests/test_rich_data/main.cpp @@ -0,0 +1,40 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + rich_data_list data; + PE_TEST_EXCEPTION(data = get_rich_data(image), "Rich Data test 1", test_level_critical); + PE_TEST(data.size() == 8, "Rich Data test 2", test_level_normal); + PE_TEST(data[0].get_number() == 158, "Rich Data test 3", test_level_normal); + + if(image.get_pe_type() == pe_type_32) + { + PE_TEST(data[1].get_times() == 47, "Rich Data test 4", test_level_normal); + } + else + { + PE_TEST(data[1].get_times() == 48, "Rich Data test 4", test_level_normal); + } + + PE_TEST(data[2].get_version() == 40219, "Rich Data test 5", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_rich_data/test_rich_data.vcproj b/tests/test_rich_data/test_rich_data.vcproj new file mode 100644 index 0000000..5739950 --- /dev/null +++ b/tests/test_rich_data/test_rich_data.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_rich_data" + ProjectGUID="{1F877026-3D94-41BF-B392-06DFAF67AE34}" + RootNamespace="test_rich_data" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_rich_data/test_rich_data.vcxproj b/tests/test_rich_data/test_rich_data.vcxproj new file mode 100644 index 0000000..37b297b --- /dev/null +++ b/tests/test_rich_data/test_rich_data.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{114AC59B-BC28-40DB-8380-67C422D0C81B}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_rich_data</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_rich_data/test_rich_data.vcxproj.filters b/tests/test_rich_data/test_rich_data.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_rich_data/test_rich_data.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_runner/Makefile b/tests/test_runner/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_runner/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_runner/main.cpp b/tests/test_runner/main.cpp new file mode 100644 index 0000000..5600e50 --- /dev/null +++ b/tests/test_runner/main.cpp @@ -0,0 +1,212 @@ +#include <iostream> +#include <map> +#include <string> +#include <stdio.h> +#include <pe_bliss.h> +#ifndef PE_BLISS_WINDOWS +#include <sys/wait.h> +#endif + +#define PE_TEST_32 "../pe_files/image32.exe" +#define PE_TEST_64 "../pe_files/image64.exe" +#define PE_TEST_DOTNET "../pe_files/TestApp.exe" +#define PE_TEST_DEBUG "../pe_files/debug_test.exe" +#define PE_DLL_TEST_32 "../pe_files/test_dll_32.dll" +#define PE_DLL_TEST_64 "../pe_files/test_dll_64.dll" +#define PE_BOUND_IMPORT_TEST_32 "../pe_files/bound32.exe" +#define PE_BOUND_IMPORT_TEST_64 "../pe_files/bound64.exe" +#define PE_TEST_MESSAGE_TABLE "../pe_files/message_table_resource.exe" + +class testcase +{ +public: + testcase(const std::string& binary_name, const std::string& testcase_name, const std::vector<std::string>& command_lines = std::vector<std::string>()) + :binary_name_(binary_name), testcase_name_(testcase_name), command_lines_(command_lines) + {} + + const std::string get_binary_name() const + { +#ifdef PE_BLISS_WINDOWS + return binary_name_ + ".exe"; +#else + return binary_name_; +#endif + } + + const std::string& get_testcase_name() const + { + return testcase_name_; + } + + const std::vector<std::string>& get_command_lines() const + { + return command_lines_; + } + +private: + std::string binary_name_; + std::string testcase_name_; + std::vector<std::string> command_lines_; +}; + +#ifdef PE_BLISS_WINDOWS +#define POPEN _popen +#define PCLOSE _pclose +#define DEV_NULL " 1> nul" +#else +#define POPEN popen +#define PCLOSE pclose +#define DEV_NULL " 1> /dev/null" +#endif + +bool run_test(const std::string& path, const std::string& test, bool& warnings_occured, const std::string& cmd = "") +{ + FILE* bin; +#ifdef PE_BLISS_WINDOWS + bin = POPEN(("\"\"" + path + test + "\" \"" + path + cmd + "\"\" 2>&1" + DEV_NULL).c_str(), "r"); +#else + bin = POPEN(("\"" + path + test + "\" \"" + path + cmd + "\" 2>&1" + DEV_NULL).c_str(), "r"); +#endif + + if(bin == NULL) + { + std::cerr << "Cannot execute testsuite" << std::endl; + return false; + } + + char buf[256]; + while(fgets(buf, sizeof(buf), bin) != NULL) + { + warnings_occured = true; + std::cerr << buf; + } + +#ifdef PE_BLISS_WINDOWS + return PCLOSE(bin) == 0; +#else + int stat; + int wstat = WEXITSTATUS(stat = PCLOSE(bin)); + if(stat < 0 || (wstat != 0 && wstat != 128 + SIGPIPE)) + return false; + else + return true; +#endif +} + +const std::string get_full_path(const std::string& full_name) +{ + std::string::size_type slash_pos = full_name.find_last_of("/\\"); + if(slash_pos != std::string::npos) + return full_name.substr(0, slash_pos + 1); + + return ""; +} + +int main(int argc, char* argv[]) +{ + bool warnings_occured = false; + + typedef std::vector<testcase> test_list; + + test_list tests; + + { + std::vector<std::string> command_line; + command_line.push_back(PE_TEST_32); + command_line.push_back(PE_TEST_64); + tests.push_back(testcase("tests_utils", "PE Utils tests")); + tests.push_back(testcase("tests_basic", "Basic PE tests", command_line)); + tests.push_back(testcase("test_checksum", "PE Checksum tests", command_line)); + tests.push_back(testcase("test_entropy", "PE Entropy tests", command_line)); + tests.push_back(testcase("test_rich_data", "PE Rich Data tests", command_line)); + tests.push_back(testcase("test_imports", "PE Imports tests", command_line)); + tests.push_back(testcase("test_relocations", "PE Relocations tests", command_line)); + tests.push_back(testcase("test_load_config", "PE Load Configuration tests", command_line)); + tests.push_back(testcase("test_exception_directory", "PE Exception Directory tests", command_line)); + tests.push_back(testcase("test_tls", "PE Thread Local Storage tests", command_line)); + tests.push_back(testcase("test_resources", "PE Resource Directory tests", command_line)); + + command_line.pop_back(); + //These binaries work with resource classes, which are not dependent on PE format + //So PE32 is enough to test them + tests.push_back(testcase("test_resource_viewer", "PE Resource Viewer tests", command_line)); + tests.push_back(testcase("test_resource_manager", "PE Resource Manager tests", command_line)); + tests.push_back(testcase("test_resource_bitmap", "PE Resource Bitmap Read & Write tests", command_line)); + tests.push_back(testcase("test_resource_icon_cursor", "PE Resource Icon/Cursor Read & Write tests", command_line)); + tests.push_back(testcase("test_resource_string_table", "PE Resource String Table Parser tests", command_line)); + tests.push_back(testcase("test_resource_version_info", "PE Resource Version Info & Write tests", command_line)); + } + + { + std::vector<std::string> message_table_command_line; + message_table_command_line.push_back(PE_TEST_MESSAGE_TABLE); + tests.push_back(testcase("test_resource_message_table", "Pe Resource Message Table Parser tests", message_table_command_line)); + } + + { + std::vector<std::string> dotnet_command_line; + dotnet_command_line.push_back(PE_TEST_DOTNET); + tests.push_back(testcase("test_dotnet", "PE Basic .NET tests", dotnet_command_line)); + } + + { + std::vector<std::string> debug_command_line; + debug_command_line.push_back(PE_TEST_DEBUG); + debug_command_line.push_back(PE_TEST_64); + tests.push_back(testcase("test_debug", "PE Debug Info tests", debug_command_line)); + } + + { + std::vector<std::string> dll_command_line; + dll_command_line.push_back(PE_DLL_TEST_32); + dll_command_line.push_back(PE_DLL_TEST_64); + tests.push_back(testcase("test_exports", "PE Exports tests", dll_command_line)); + } + + { + std::vector<std::string> bound_import_command_line; + bound_import_command_line.push_back(PE_BOUND_IMPORT_TEST_32); + bound_import_command_line.push_back(PE_BOUND_IMPORT_TEST_64); + tests.push_back(testcase("test_bound_import", "PE Bound Import tests", bound_import_command_line)); + } + + std::string runner_path(get_full_path(argv[0])); + + for(test_list::const_iterator it = tests.begin(); it != tests.end(); ++it) + { + const testcase& t = *it; + bool result = true; + if(!t.get_command_lines().empty()) + { + const std::vector<std::string>& cmd_lines = t.get_command_lines(); + for(std::vector<std::string>::const_iterator cmd = cmd_lines.begin(); cmd != cmd_lines.end(); ++cmd) + { + std::cout << "Running " << t.get_testcase_name() << " with \"" << (*cmd) << "\"" << std::endl; + result = run_test(runner_path, t.get_binary_name(), warnings_occured, *cmd); + if(!result) + break; + } + } + else + { + std::cout << "Running " << t.get_testcase_name() << std::endl; + result = run_test(runner_path, t.get_binary_name(), warnings_occured); + } + + if(!result) + { + std::cerr << "Some tests hard-failed in this testsuite, exiting..." << std::endl; + return -1; + } + + std::cout << std::endl; + } + + if(warnings_occured) + std::cout << "Some tests failed, check the log!" << std::endl; + else + std::cout << "Everything went just fine!" << std::endl; + + std::cout << "Finished." << std::endl; + return 0; +} diff --git a/tests/test_runner/test_runner.vcproj b/tests/test_runner/test_runner.vcproj new file mode 100644 index 0000000..e9ac268 --- /dev/null +++ b/tests/test_runner/test_runner.vcproj @@ -0,0 +1,351 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_runner" + ProjectGUID="{31843E48-DC9A-4887-BD97-328079D78C88}" + RootNamespace="test_runner" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_runner/test_runner.vcxproj b/tests/test_runner/test_runner.vcxproj new file mode 100644 index 0000000..436e9ef --- /dev/null +++ b/tests/test_runner/test_runner.vcxproj @@ -0,0 +1,163 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{132DFCC9-13EF-4178-9772-1C467FB296D6}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_runner</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_runner/test_runner.vcxproj.filters b/tests/test_runner/test_runner.vcxproj.filters new file mode 100644 index 0000000..2a14256 --- /dev/null +++ b/tests/test_runner/test_runner.vcxproj.filters @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/test_tls/Makefile b/tests/test_tls/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/test_tls/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/test_tls/main.cpp b/tests/test_tls/main.cpp new file mode 100644 index 0000000..ec4e465 --- /dev/null +++ b/tests/test_tls/main.cpp @@ -0,0 +1,101 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +void test_tls(const tls_info& info, const pe_base& image, bool check_callbacks = true) +{ + PE_TEST(info.get_characteristics() == 0, "TLS test 1", test_level_normal); + PE_TEST(info.get_size_of_zero_fill() == 0, "TLS test 2", test_level_normal); + PE_TEST(info.get_raw_data_end_rva() - info.get_raw_data_start_rva() == 8, "TLS test 3", test_level_normal); + PE_TEST(info.get_raw_data() == std::string("\0\0\0\0\x37\x02\0\0", 8), "TLS test 4", test_level_normal); + + if(check_callbacks) + { + PE_TEST(info.get_tls_callbacks().empty(), "TLS test 5", test_level_normal); + } + + if(image.get_pe_type() == pe_type_32) + { + PE_TEST(info.get_index_rva() == 0x420738 - image.get_image_base_32(), "TLS test 6", test_level_normal); + + if(check_callbacks) + { + PE_TEST(info.get_callbacks_rva() == 0x418188 - image.get_image_base_32(), "TLS test 7", test_level_normal); + } + } + else + { + PE_TEST(info.get_index_rva() == 0x14002647Cull - image.get_image_base_64(), "TLS test 6", test_level_normal); + + if(check_callbacks) + { + PE_TEST(info.get_callbacks_rva() == 0x14001B310ull - image.get_image_base_64(), "TLS test 7", test_level_normal); + } + } +} + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + pe_base image(pe_factory::create_pe(*pe_file)); + + tls_info info; + PE_TEST_EXCEPTION(info = get_tls_info(image), "TLS Parser test 1", test_level_critical); + test_tls(info, image); + + section s; + s.get_raw_data().resize(1); + s.set_name("newtls"); + section& new_tls_section = image.add_section(s); + uint32_t old_tls_rva = image.get_directory_rva(pe_win::image_directory_entry_tls); + PE_TEST_EXCEPTION(rebuild_tls(image, info, new_tls_section, 0, false, false, tls_data_expand_raw, true, true), "TLS Rebuilder test 1", test_level_critical); + PE_TEST(old_tls_rva != image.get_directory_rva(pe_win::image_directory_entry_tls), "TLS directory test", test_level_normal); + + PE_TEST_EXCEPTION(info = get_tls_info(image), "TLS Parser test 2", test_level_critical); + test_tls(info, image); + + new_tls_section.set_raw_data("111"); + PE_TEST_EXCEPTION(rebuild_tls(image, info, new_tls_section, 3, false, false, tls_data_expand_raw, true, true), "TLS Rebuilder test 2", test_level_critical); + PE_TEST_EXCEPTION(info = get_tls_info(image), "TLS Parser test 3", test_level_critical); + PE_TEST(new_tls_section.get_raw_data().substr(0, 3) == "111", "TLS Rebuilder offset test", test_level_normal); + test_tls(info, image); + + PE_TEST_EXCEPTION(rebuild_tls(image, info, new_tls_section, 12, false, false, tls_data_expand_raw, true, true), "TLS Rebuilder test 3", test_level_critical); + PE_TEST_EXCEPTION(info = get_tls_info(image), "TLS Parser test 4", test_level_critical); + test_tls(info, image); + + image.set_section_virtual_size(new_tls_section, 0x2000); + info.set_raw_data_start_rva(image.rva_from_section_offset(new_tls_section, 0x1000)); + info.recalc_raw_data_end_rva(); + PE_TEST_EXCEPTION(rebuild_tls(image, info, new_tls_section, 12, false, true, tls_data_expand_raw, true, true), "TLS Rebuilder test 4", test_level_critical); + PE_TEST_EXCEPTION(info = get_tls_info(image), "TLS Parser test 5", test_level_critical); + test_tls(info, image); + + info.add_tls_callback(0x111); + info.add_tls_callback(0x222); + info.add_tls_callback(0x333); + info.add_tls_callback(0x444); + info.add_tls_callback(0x555); + info.set_callbacks_rva(image.rva_from_section_offset(new_tls_section, 0x1500)); + + PE_TEST_EXCEPTION(rebuild_tls(image, info, new_tls_section, 12, true, true, tls_data_expand_raw, true, true), "TLS Rebuilder test 5", test_level_critical); + PE_TEST_EXCEPTION(info = get_tls_info(image), "TLS Parser test 6", test_level_critical); + test_tls(info, image, false); + PE_TEST(info.get_tls_callbacks().size() == 5, "TLS test 7", test_level_normal); + PE_TEST(info.get_tls_callbacks()[2] == 0x333, "TLS test 8", test_level_normal); + + PE_TEST_END + + return 0; +} diff --git a/tests/test_tls/test_tls.vcproj b/tests/test_tls/test_tls.vcproj new file mode 100644 index 0000000..5334447 --- /dev/null +++ b/tests/test_tls/test_tls.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="test_tls" + ProjectGUID="{63D80BC8-EB14-4698-A391-4A41AC15E8D1}" + RootNamespace="test_tls" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/test_tls/test_tls.vcxproj b/tests/test_tls/test_tls.vcxproj new file mode 100644 index 0000000..f528514 --- /dev/null +++ b/tests/test_tls/test_tls.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{CFC22F11-2C5F-46F3-9C51-ED8C3E5EFA89}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test_tls</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/test_tls/test_tls.vcxproj.filters b/tests/test_tls/test_tls.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/test_tls/test_tls.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/tests.mak b/tests/tests.mak new file mode 100644 index 0000000..dee47fe --- /dev/null +++ b/tests/tests.mak @@ -0,0 +1,23 @@ +PWD=$(shell pwd) +OUTDIR = ../bin/ +LIBPATH = ../../lib/libpebliss.a +NAME=$(shell basename $(PWD)) +CXXFLAGS = -O2 -Wall -I../../pe_lib -I../ + +ifdef PE_DEBUG +CXXFLAGS += -g -O0 +endif + +all: $(OUTDIR)$(NAME) + +clean: + rm -f $(NAME) *.o + rm -f $(OUTDIR)$(NAME) + +$(NAME): main.o + $(CXX) -Wall $^ -lpebliss -L../../lib -o $(NAME) + +main.o: $(LIBPATH) + +$(OUTDIR)$(NAME): $(NAME) + cp -d $(NAME) $(OUTDIR) diff --git a/tests/tests_basic/Makefile b/tests/tests_basic/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/tests_basic/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/tests_basic/main.cpp b/tests/tests_basic/main.cpp new file mode 100644 index 0000000..26a4591 --- /dev/null +++ b/tests/tests_basic/main.cpp @@ -0,0 +1,496 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + std::auto_ptr<std::ifstream> pe_file; + if(!open_pe_file(argc, argv, pe_file)) + return -1; + + std::auto_ptr<pe_base> image; + PE_TEST_EXCEPTION(image.reset(new pe_base(pe_factory::create_pe(*pe_file))), "Creation, type detection and copying test", test_level_critical); + + PE_TEST(!image->has_overlay(), "Overlay test", test_level_normal); + PE_TEST(image->get_stub_overlay()[1] == 0x1F, "Stub test 1", test_level_normal); + PE_TEST_EXCEPTION(image->fill_stub_overlay(0x11), "Stub test 2", test_level_normal); + PE_TEST(image->get_stub_overlay()[1] == 0x11, "Stub test 3", test_level_normal); + PE_TEST_EXCEPTION(image->strip_stub_overlay(), "Stub test 4", test_level_normal); + PE_TEST(image->get_stub_overlay().empty(), "Stub test 5", test_level_normal); + + std::cout << "PE Header tests..." << std::endl; + + PE_TEST(!image->directory_exists(pe_win::image_directory_entry_com_descriptor), "Directory test 1", test_level_normal); + PE_TEST(image->directory_exists(pe_win::image_directory_entry_import), "Directory test 2", test_level_normal); + PE_TEST(image->has_imports(), "Directory test 3", test_level_normal); + PE_TEST(!image->has_exports(), "Directory test 4", test_level_normal); + + PE_TEST(image->get_subsystem() == pe_win::image_subsystem_windows_cui, "Subsystem test 1", test_level_normal); + PE_TEST(image->is_console(), "Subsystem test 2", test_level_normal); + PE_TEST(!image->is_gui(), "Subsystem test 3", test_level_normal); + + image->set_subsystem(pe_win::image_subsystem_native_windows); + PE_TEST(image->get_subsystem() == pe_win::image_subsystem_native_windows, "Subsystem test 4", test_level_normal); + + PE_TEST(image->get_pe_signature() == 0x4550, "PE Signature test", test_level_normal); + + PE_TEST(image->get_file_alignment() == 0x200, "File Alignment test 1", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->set_file_alignment(123), pe_exception::incorrect_file_alignment, "File Alignment test 2", test_level_normal); + PE_TEST_EXCEPTION(image->set_file_alignment(0x1000), "File Alignment test 3", test_level_normal); + PE_TEST(image->get_file_alignment() == 0x1000, "File Alignment test 4", test_level_normal); + + PE_TEST(image->get_section_alignment() == 0x1000, "Section Alignment test", test_level_normal); + + PE_TEST(image->get_number_of_rvas_and_sizes() == 0x10, "Data directories test", test_level_normal); + + PE_TEST(image->check_characteristics_flag(pe_win::image_file_executable_image), "Image Characteristics test 1", test_level_normal); + PE_TEST(!image->check_characteristics_flag(pe_win::image_file_dll), "Image Characteristics test 2", test_level_normal); + + PE_TEST(image->get_size_of_headers() == 0x400, "Size of headers test", test_level_normal); + + PE_TEST(image->get_dll_characteristics() == 0x8140, "Image DLL Characteristics test", test_level_normal); + + if(image->get_pe_type() == pe_type_32) + { + PE_TEST(image->get_directory_rva(pe_win::image_directory_entry_import) == 0x1C9E4, "Directory RVA test 1", test_level_normal); + PE_TEST(image->get_directory_size(pe_win::image_directory_entry_import) == 0x3C, "Directory size test 1", test_level_normal); + + PE_TEST(image->get_minor_os_version() == 1 && image->get_major_os_version() == 5, "OS Version test", test_level_normal); + PE_TEST(image->get_minor_subsystem_version() == 1 && image->get_major_subsystem_version() == 5, "Subsystem Version test", test_level_normal); + + PE_TEST(image->get_pe_header_start() == 0xE0, "e_lfanew test", test_level_normal); + + PE_TEST(image->get_size_of_image() == 0x41000, "Size of Image test", test_level_normal); + PE_TEST(image->get_ep() == 0x6851, "Entry Point test", test_level_normal); + + PE_TEST(image->get_characteristics() == 0x102, "Image Characteristics test 3", test_level_normal); + + PE_TEST(image->get_size_of_optional_header() == 0xE0, "Size of optional header test", test_level_normal); + + PE_TEST(image->get_magic() == 0x10B, "PE Magic test", test_level_normal); + + PE_TEST(image->get_image_base_32() == 0x400000, "Image Base test 1", test_level_normal); + + { + uint32_t image_base; + image->get_image_base(image_base); + PE_TEST(image_base == 0x400000, "Image Base test 2", test_level_normal); + } + + PE_TEST(image->get_heap_size_commit_32() == 0x1000, "Heap Size Commit test 1", test_level_normal); + PE_TEST(image->get_heap_size_reserve_32() == 0x100000, "Heap Size Reserve test 1", test_level_normal); + PE_TEST(image->get_stack_size_commit_32() == 0x1000, "Stack Size Commit test 1", test_level_normal); + PE_TEST(image->get_stack_size_reserve_32() == 0x100000, "Stack Size Reserve test 1", test_level_normal); + + { + uint32_t size; + image->get_heap_size_commit(size); + PE_TEST(size == 0x1000, "Heap Size Commit test 2", test_level_normal); + image->get_heap_size_reserve(size); + PE_TEST(size == 0x100000, "Heap Size Reserve test 2", test_level_normal); + image->get_stack_size_commit(size); + PE_TEST(size == 0x1000, "Stack Size Commit test 2", test_level_normal); + image->get_stack_size_reserve(size); + PE_TEST(size == 0x100000, "Stack Size Reserve test 2", test_level_normal); + } + + PE_TEST(image->get_time_date_stamp() == 0x508E65A3, "TimeStamp test", test_level_normal); + PE_TEST(image->get_machine() == 0x014C, "Machine test", test_level_normal); + } + else + { + PE_TEST(image->get_directory_rva(pe_win::image_directory_entry_import) == 0x223EC, "Directory RVA test", test_level_normal); + PE_TEST(image->get_directory_size(pe_win::image_directory_entry_import) == 0x3C, "Directory size test", test_level_normal); + + PE_TEST(image->get_pe_header_start() == 0xF0, "e_lfanew test", test_level_normal); + + PE_TEST(image->get_minor_os_version() == 2 && image->get_major_os_version() == 5, "OS Version test", test_level_normal); + PE_TEST(image->get_minor_subsystem_version() == 2 && image->get_major_subsystem_version() == 5, "Subsystem Version test", test_level_normal); + + PE_TEST(image->get_size_of_image() == 0x48000, "Size of Image test", test_level_normal); + PE_TEST(image->get_ep() == 0x7A64, "Entry Point test", test_level_normal); + + PE_TEST(image->get_characteristics() == 0x22, "Image Characteristics test 3", test_level_normal); + + PE_TEST(image->get_size_of_optional_header() == 0xF0, "Size of optional header test", test_level_normal); + + PE_TEST(image->get_magic() == 0x20B, "PE Magic test", test_level_normal); + + PE_TEST(image->get_image_base_64() == 0x0000000140000000ull, "Image Base test 1", test_level_normal); + + { + uint64_t image_base; + image->get_image_base(image_base); + PE_TEST(image_base == 0x0000000140000000ull, "Image Base test 2", test_level_normal); + } + + PE_TEST(image->get_heap_size_commit_64() == 0x1000, "Heap Size Commit test 1", test_level_normal); + PE_TEST(image->get_heap_size_reserve_64() == 0x100000, "Heap Size Reserve test 1", test_level_normal); + PE_TEST(image->get_stack_size_commit_64() == 0x1000, "Stack Size Commit test 1", test_level_normal); + PE_TEST(image->get_stack_size_reserve_64() == 0x100000, "Stack Size Reserve test 1", test_level_normal); + + { + uint64_t size; + image->get_heap_size_commit(size); + PE_TEST(size == 0x1000, "Heap Size Commit test 2", test_level_normal); + image->get_heap_size_reserve(size); + PE_TEST(size == 0x100000, "Heap Size Reserve test 2", test_level_normal); + image->get_stack_size_commit(size); + PE_TEST(size == 0x1000, "Stack Size Commit test 2", test_level_normal); + image->get_stack_size_reserve(size); + PE_TEST(size == 0x100000, "Stack Size Reserve test 2", test_level_normal); + } + + PE_TEST(image->get_time_date_stamp() == 0x508E65B2, "TimeStamp test", test_level_normal); + PE_TEST(image->get_machine() == 0x8664, "Machine test", test_level_normal); + } + + image->set_directory_rva(pe_win::image_directory_entry_architecture, 0x1000); + image->set_directory_size(pe_win::image_directory_entry_architecture, 0x2000); + PE_TEST(image->get_directory_rva(pe_win::image_directory_entry_architecture) == 0x1000, "Directory RVA test 2", test_level_normal); + PE_TEST(image->get_directory_size(pe_win::image_directory_entry_architecture) == 0x2000, "Directory size test 2", test_level_normal); + + image->remove_directory(pe_win::image_directory_entry_architecture); + PE_TEST(image->get_directory_rva(pe_win::image_directory_entry_architecture) == 0, "Directory RVA test 3", test_level_normal); + PE_TEST(image->get_directory_size(pe_win::image_directory_entry_architecture) == 0, "Directory size test 3", test_level_normal); + + PE_TEST(image->strip_data_directories(0, false) == 0x10 - 3, "Data directories strip test 1", test_level_normal); + PE_TEST(image->get_number_of_rvas_and_sizes() == 0x10 - 3, "Data directories strip test 2", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->get_directory_rva(pe_win::image_directory_entry_com_descriptor) == 0, pe_exception::directory_does_not_exist, "Directory RVA test 4", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->get_directory_size(pe_win::image_directory_entry_com_descriptor) == 0, pe_exception::directory_does_not_exist, "Directory size test 4", test_level_normal); + + if(image->get_pe_type() == pe_type_32) + { + PE_TEST(image->strip_data_directories(0, true) == 0x10 - 5, "Data directories strip test 3", test_level_normal); + } + else + { + PE_TEST(image->strip_data_directories(0, true) == 0x10 - 6, "Data directories strip test 3", test_level_normal); + } + + std::cout << "Address tests..." << std::endl; + + if(image->get_pe_type() == pe_type_32) + { + PE_TEST(image->va_to_rva(image->get_image_base_32() + 1) == 1, "Address conversion test 1", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->va_to_rva(image->get_image_base_32() - 1), pe_exception::incorrect_address_conversion, "Address conversion test 2", test_level_normal); + PE_TEST(image->rva_to_va_32(1) == image->get_image_base_32() + 1, "Address conversion test 3", test_level_normal); + + { + uint32_t va; + image->rva_to_va(1, va); + PE_TEST(va == image->get_image_base_32() + 1, "Address conversion test 4", test_level_normal); + } + } + else + { + PE_TEST(image->va_to_rva(image->get_image_base_64() + 1) == 1, "Address conversion test 1", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->va_to_rva(image->get_image_base_64() - 1), pe_exception::incorrect_address_conversion, "Address conversion test 2", test_level_normal); + PE_TEST(image->rva_to_va_64(1) == image->get_image_base_64() + 1, "Address conversion test 3", test_level_normal); + + { + uint64_t va; + image->rva_to_va(1, va); + PE_TEST(va == image->get_image_base_64() + 1, "Address conversion test 4", test_level_normal); + } + } + + PE_TEST(image->rva_to_file_offset(0x1001) == 0x401, "Address conversion test 5", test_level_normal); + PE_TEST(image->file_offset_to_rva(0x401) == 0x1001, "Address conversion test 6", test_level_normal); + PE_TEST(image->file_offset_to_rva(0x1) == 0x1, "Address conversion test 7", test_level_normal); + PE_TEST(image->rva_to_file_offset(0x1) == 0x1, "Address conversion test 8", test_level_normal); + + + std::cout << "Section tests..." << std::endl; + if(image->get_pe_type() == pe_type_32) + { + PE_TEST(image->get_number_of_sections() == 0x6, "Section number test 1", test_level_normal); + } + else + { + PE_TEST(image->get_number_of_sections() == 0x7, "Section number test 1", test_level_normal); + } + + PE_TEST(image->get_image_sections().size() == image->get_number_of_sections(), "Section number test 2", test_level_critical); + + PE_TEST(image->get_image_sections().at(0).get_name() == ".text", "Section name test 1", test_level_normal); + PE_TEST(image->get_image_sections().at(1).get_name() == ".rdata", "Section name test 2", test_level_normal); + + PE_TEST(image->section_from_directory(pe_win::image_directory_entry_import).get_name() == ".rdata", "Section test 1", test_level_normal); + PE_TEST(image->section_from_rva(0x1000).get_name() == ".text", "Section test 2", test_level_normal); + PE_TEST(image->section_from_va(image->get_image_base_64() + 0x1000).get_name() == ".text", "Section test 3", test_level_normal); + PE_TEST(image->section_from_file_offset(0x401).get_name() == ".text", "Section test 4", test_level_normal); + PE_TEST(image->rva_from_section_offset(image->get_image_sections().at(0), 0x5) == 0x1005, "Section test 5", test_level_normal); + + { + const section& s = image->get_image_sections().at(0); + PE_TEST(image->section_data_length_from_rva(s.get_virtual_address() + 123, section_data_raw, false) == s.get_raw_data().size(), "Section test 6", test_level_normal); + PE_TEST(image->section_data_length_from_rva(s, s.get_virtual_address() + 123, section_data_raw) == s.get_raw_data().size() - 123, "Section test 7", test_level_normal); + PE_TEST(image->section_data_length_from_rva(s.get_virtual_address() + 123, s.get_virtual_address() + 123, section_data_raw, false) == s.get_raw_data().size() - 123, "Section test 8", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_rva(s, s.get_virtual_address() - 1, section_data_raw), pe_exception::rva_not_exists, "Section test 9", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_rva(s.get_virtual_address() + 123, s.get_virtual_address() - 1, section_data_raw, false), pe_exception::rva_not_exists, "Section test 10", test_level_normal); + + PE_TEST(image->section_data_length_from_rva(s.get_virtual_address() + 123, section_data_virtual, false) == s.get_aligned_virtual_size(image->get_section_alignment()), "Section test 11", test_level_normal); + PE_TEST(image->section_data_length_from_rva(s, s.get_virtual_address() + 123, section_data_virtual) == s.get_aligned_virtual_size(image->get_section_alignment()) - 123, "Section test 12", test_level_normal); + PE_TEST(image->section_data_length_from_rva(s.get_virtual_address() + 123, s.get_virtual_address() + 123, section_data_virtual, false) == s.get_aligned_virtual_size(image->get_section_alignment()) - 123, "Section test 13", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_rva(s, s.get_virtual_address() - 1, section_data_virtual), pe_exception::rva_not_exists, "Section test 14", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_rva(s.get_virtual_address() + 123, s.get_virtual_address() - 1, section_data_virtual, false), pe_exception::rva_not_exists, "Section test 15", test_level_normal); + + if(image->get_pe_type() == pe_type_32) + { + uint32_t base = image->get_image_base_32(); + PE_TEST(image->section_data_length_from_va(base + s.get_virtual_address() + 123, section_data_raw, false) == s.get_raw_data().size(), "Section test 16", test_level_normal); + PE_TEST(image->section_data_length_from_va(s, base + s.get_virtual_address() + 123, section_data_raw) == s.get_raw_data().size() - 123, "Section test 17", test_level_normal); + PE_TEST(image->section_data_length_from_va(base + s.get_virtual_address() + 123, base + s.get_virtual_address() + 123, section_data_raw, false) == s.get_raw_data().size() - 123, "Section test 18", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_va(s, base + s.get_virtual_address() - 1, section_data_raw), pe_exception::rva_not_exists, "Section test 19", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_va(base + s.get_virtual_address() + 123, base + s.get_virtual_address() - 1, section_data_raw, false), pe_exception::rva_not_exists, "Section test 20", test_level_normal); + + PE_TEST(image->section_data_length_from_va(base + s.get_virtual_address() + 123, section_data_virtual, false) == s.get_aligned_virtual_size(image->get_section_alignment()), "Section test 21", test_level_normal); + PE_TEST(image->section_data_length_from_va(s, base + s.get_virtual_address() + 123, section_data_virtual) == s.get_aligned_virtual_size(image->get_section_alignment()) - 123, "Section test 22", test_level_normal); + PE_TEST(image->section_data_length_from_va(base + s.get_virtual_address() + 123, base + s.get_virtual_address() + 123, section_data_virtual, false) == s.get_aligned_virtual_size(image->get_section_alignment()) - 123, "Section test 23", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_va(s, base + s.get_virtual_address() - 1, section_data_virtual), pe_exception::rva_not_exists, "Section test 24", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_va(base + s.get_virtual_address() + 123, base + s.get_virtual_address() - 1, section_data_virtual, false), pe_exception::rva_not_exists, "Section test 25", test_level_normal); + + PE_TEST(image->section_data_from_rva<uint32_t>(0x1005, section_data_raw, false) == 0x505BE900, "Section data test 1", test_level_normal); + PE_TEST(image->section_data_from_rva<uint32_t>(s, 0x1005, section_data_raw) == 0x505BE900, "Section data test 2", test_level_normal); + + PE_TEST(image->section_data_from_va<uint32_t>(base + 0x1005, section_data_raw, false) == 0x505BE900, "Section data test 3", test_level_normal); + PE_TEST(image->section_data_from_va<uint32_t>(s, base + 0x1005, section_data_raw) == 0x505BE900, "Section data test 4", test_level_normal); + + PE_TEST(image->section_data_from_rva<uint32_t>(0x1005, section_data_virtual, false) == 0x505BE900, "Section data test 5", test_level_normal); + PE_TEST(image->section_data_from_rva<uint32_t>(s, 0x1005, section_data_virtual) == 0x505BE900, "Section data test 6", test_level_normal); + + PE_TEST(image->section_data_from_va<uint32_t>(base + 0x1005, section_data_virtual, false) == 0x505BE900, "Section data test 7", test_level_normal); + PE_TEST(image->section_data_from_va<uint32_t>(s, base + 0x1005, section_data_virtual) == 0x505BE900, "Section data test 8", test_level_normal); + + PE_TEST(image->section_data_from_rva<uint32_t>(0x1, section_data_raw, true) == 0x0300905A, "Section data test 9", test_level_normal); + PE_TEST(image->section_data_from_va<uint32_t>(base + 0x1, section_data_raw, true) == 0x0300905A, "Section data test 10", test_level_normal); + + PE_TEST(*image->section_data_from_rva(0x1005, section_data_raw, false) == 0x00, "Section data test 11", test_level_normal); + PE_TEST(*image->section_data_from_rva(s, 0x1005, section_data_raw) == 0x00, "Section data test 12", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->section_data_from_rva(s, 0x999, section_data_raw), pe_exception::rva_not_exists, "Section data test 13", test_level_normal); + + PE_TEST(*image->section_data_from_va(base + 0x1005, section_data_raw, false) == 0x00, "Section data test 14", test_level_normal); + PE_TEST(*image->section_data_from_va(s, base + 0x1005, section_data_raw) == 0x00, "Section data test 15", test_level_normal); + + PE_TEST(*image->section_data_from_rva(0x1E000 + 0x388C, section_data_virtual, false) == 0x00, "Section data test 16", test_level_normal); + } + else + { + uint64_t base = image->get_image_base_64(); + PE_TEST(image->section_data_length_from_va(base + s.get_virtual_address() + 123, section_data_raw, false) == s.get_raw_data().size(), "Section test 16", test_level_normal); + PE_TEST(image->section_data_length_from_va(s, base + s.get_virtual_address() + 123, section_data_raw) == s.get_raw_data().size() - 123, "Section test 17", test_level_normal); + PE_TEST(image->section_data_length_from_va(base + s.get_virtual_address() + 123, base + s.get_virtual_address() + 123, section_data_raw, false) == s.get_raw_data().size() - 123, "Section test 18", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_va(s, base + s.get_virtual_address() - 1, section_data_raw), pe_exception::rva_not_exists, "Section test 19", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_va(base + s.get_virtual_address() + 123, base + s.get_virtual_address() - 1, section_data_raw, false), pe_exception::rva_not_exists, "Section test 20", test_level_normal); + + PE_TEST(image->section_data_length_from_va(base + s.get_virtual_address() + 123, section_data_virtual, false) == s.get_aligned_virtual_size(image->get_section_alignment()), "Section test 21", test_level_normal); + PE_TEST(image->section_data_length_from_va(s, base + s.get_virtual_address() + 123, section_data_virtual) == s.get_aligned_virtual_size(image->get_section_alignment()) - 123, "Section test 22", test_level_normal); + PE_TEST(image->section_data_length_from_va(base + s.get_virtual_address() + 123, base + s.get_virtual_address() + 123, section_data_virtual, false) == s.get_aligned_virtual_size(image->get_section_alignment()) - 123, "Section test 23", test_level_normal); + + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_va(s, base + s.get_virtual_address() - 1, section_data_virtual), pe_exception::rva_not_exists, "Section test 24", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->section_data_length_from_va(base + s.get_virtual_address() + 123, base + s.get_virtual_address() - 1, section_data_virtual, false), pe_exception::rva_not_exists, "Section test 25", test_level_normal); + + PE_TEST(image->section_data_from_rva<uint32_t>(0x1005, section_data_raw, false) == 0x89480001, "Section data test 1", test_level_normal); + PE_TEST(image->section_data_from_rva<uint32_t>(s, 0x1005, section_data_raw) == 0x89480001, "Section data test 2", test_level_normal); + + PE_TEST(image->section_data_from_va<uint32_t>(base + 0x1005, section_data_raw, false) == 0x89480001, "Section data test 3", test_level_normal); + PE_TEST(image->section_data_from_va<uint32_t>(s, base + 0x1005, section_data_raw) == 0x89480001, "Section data test 4", test_level_normal); + + PE_TEST(image->section_data_from_rva<uint32_t>(0x1005, section_data_virtual, false) == 0x89480001, "Section data test 5", test_level_normal); + PE_TEST(image->section_data_from_rva<uint32_t>(s, 0x1005, section_data_virtual) == 0x89480001, "Section data test 6", test_level_normal); + + PE_TEST(image->section_data_from_va<uint32_t>(base + 0x1005, section_data_virtual, false) == 0x89480001, "Section data test 7", test_level_normal); + PE_TEST(image->section_data_from_va<uint32_t>(s, base + 0x1005, section_data_virtual) == 0x89480001, "Section data test 8", test_level_normal); + + PE_TEST(image->section_data_from_rva<uint32_t>(0x1, section_data_raw, true) == 0x0300905A, "Section data test 9", test_level_normal); + PE_TEST(image->section_data_from_va<uint32_t>(base + 0x1, section_data_raw, true) == 0x0300905A, "Section data test 10", test_level_normal); + + PE_TEST(*image->section_data_from_rva(0x1005, section_data_raw, false) == 0x01, "Section data test 11", test_level_normal); + PE_TEST(*image->section_data_from_rva(s, 0x1005, section_data_raw) == 0x01, "Section data test 12", test_level_normal); + PE_TEST_EXPECT_EXCEPTION(image->section_data_from_rva(s, 0x999, section_data_raw), pe_exception::rva_not_exists, "Section data test 13", test_level_normal); + + PE_TEST(*image->section_data_from_va(base + 0x1005, section_data_raw, false) == 0x01, "Section data test 14", test_level_normal); + PE_TEST(*image->section_data_from_va(s, base + 0x1005, section_data_raw) == 0x01, "Section data test 15", test_level_normal); + + PE_TEST(*image->section_data_from_rva(0x23000 + 0x46F0, section_data_virtual, false) == 0x00, "Section data test 16", test_level_normal); + } + } + + PE_TEST(image->section_and_offset_from_rva(0x1005).first == 5, "Section data test 17", test_level_normal); + PE_TEST(image->section_and_offset_from_rva(0x1005).second->get_name() == ".text", "Section data test 18", test_level_normal); + + PE_TEST(image->section_data_length_from_rva(1, section_data_raw, true) == image->get_size_of_headers(), "Section test 26", test_level_normal); + PE_TEST(image->section_data_length_from_rva(1, 1, section_data_raw, true) == image->get_size_of_headers() - 1, "Section test 27", test_level_normal); + + if(image->get_pe_type() == pe_type_32) + { + uint32_t base = image->get_image_base_32(); + PE_TEST(image->section_data_length_from_va(base + 1, section_data_raw, true) == image->get_size_of_headers(), "Section test 28", test_level_normal); + PE_TEST(image->section_data_length_from_va(base + 1, base + 1, section_data_raw, true) == image->get_size_of_headers() - 1, "Section test 29", test_level_normal); + } + else + { + uint64_t base = image->get_image_base_64(); + PE_TEST(image->section_data_length_from_va(base + 1, section_data_raw, true) == image->get_size_of_headers(), "Section test 28", test_level_normal); + PE_TEST(image->section_data_length_from_va(base + 1, base + 1, section_data_raw, true) == image->get_size_of_headers() - 1, "Section test 29", test_level_normal); + } + + PE_TEST(image->section_attached(image->get_image_sections().at(0)), "Section data test 30", test_level_normal); + PE_TEST(!image->section_attached(section()), "Section data test 31", test_level_normal); + + + { + const section& s = image->get_image_sections().at(0); + PE_TEST(s.get_characteristics() == 0x60000020, "Section class test 1", test_level_normal); + PE_TEST(s.get_name() == ".text", "Section class test 2", test_level_normal); + PE_TEST(s.get_pointer_to_raw_data() == 0x400, "Section class test 3", test_level_normal); + PE_TEST(s.get_virtual_address() == 0x1000, "Section class test 4", test_level_normal); + + if(image->get_pe_type() == pe_type_32) + { + PE_TEST(s.get_size_of_raw_data() == 0x16E00, "Section class test 5", test_level_normal); + PE_TEST(s.get_virtual_size() == 0x16C0D, "Section class test 6", test_level_normal); + } + else + { + PE_TEST(s.get_size_of_raw_data() == 0x19400, "Section class test 5", test_level_normal); + PE_TEST(s.get_virtual_size() == 0x1923E, "Section class test 6", test_level_normal); + } + + PE_TEST(s.readable(), "Section class test 7", test_level_normal); + PE_TEST(s.executable(), "Section class test 8", test_level_normal); + PE_TEST(!s.writeable(), "Section class test 9", test_level_normal); + PE_TEST(!s.shared(), "Section class test 10", test_level_normal); + PE_TEST(!s.discardable(), "Section class test 11", test_level_normal); + PE_TEST(!s.empty(), "Section class test 12", test_level_normal); + } + + { + section s; + PE_TEST_EXPECT_EXCEPTION(image->prepare_section(s), pe_exception::zero_section_sizes, "Prepare Section test 1", test_level_normal); + } + + { + section s; + s.set_raw_data("123"); + PE_TEST_EXCEPTION(image->prepare_section(s), "Prepare Section test 2", test_level_normal); + PE_TEST(s.get_virtual_size() == pe_utils::align_up(s.get_size_of_raw_data(), image->get_file_alignment()), "Prepare Section test 3", test_level_normal); + + uint16_t old_sections_count = image->get_number_of_sections(); + uint16_t old_size_of_image = image->get_size_of_image(); + PE_TEST_EXCEPTION(image->add_section(s), "Add section test 1", test_level_normal); + PE_TEST(image->get_number_of_sections() == old_sections_count + 1, "Add section test 2", test_level_normal); + PE_TEST(image->get_image_sections().back().get_raw_data() == "123", "Add section test 3", test_level_normal); + PE_TEST(image->get_size_of_image() > old_size_of_image, "Add section test 4", test_level_normal); + } + + { + section s; + s.set_raw_data(std::string("123\0\0\0", 6)); + PE_TEST_EXCEPTION(image->recalculate_section_sizes(s, true), "recalculate_section_sizes test 1", test_level_normal); + PE_TEST(s.get_raw_data() == "123", "recalculate_section_sizes test 2", test_level_normal); + } + + PE_TEST_EXPECT_EXCEPTION(image->set_section_virtual_size(image->get_image_sections().at(0), 0x100), pe_exception::error_changing_section_virtual_size, "set_section_virtual_size test 1", test_level_normal); + + { + section s; + PE_TEST_EXPECT_EXCEPTION(image->set_section_virtual_size(s, 0), pe_exception::error_changing_section_virtual_size, "set_section_virtual_size test 2", test_level_normal); + } + + PE_TEST_EXCEPTION(image->set_section_virtual_size(image->get_image_sections().back(), 0x1000), "set_section_virtual_size test 3", test_level_normal); + PE_TEST(image->get_image_sections().back().get_virtual_size() == 0x1000, "set_section_virtual_size test 4", test_level_normal); + + image->set_file_alignment(0x1000); + PE_TEST_EXCEPTION(image->realign_all_sections(), "Section realigning test", test_level_normal); + PE_TEST_EXCEPTION(image->realign_file(0x200), "File realigning test", test_level_normal); + + { + section& s = image->get_image_sections().back(); + PE_TEST_EXPECT_EXCEPTION(image->expand_section(s, s.get_virtual_address() + 0x5000, 0x1000, pe_base::expand_section_raw), pe_exception::rva_not_exists, "Section expand test 1", test_level_normal); + PE_TEST(image->expand_section(s, s.get_virtual_address() + 0x100, 0x1000, pe_base::expand_section_raw) == true, "Section expand test 2", test_level_normal); + PE_TEST(s.get_virtual_size() >= 0x100 + 0x1000, "Section expand test 3", test_level_normal); + PE_TEST(s.get_size_of_raw_data() >= 0x100 + 0x1000, "Section expand test 4", test_level_normal); + + uint32_t old_raw_size = s.get_size_of_raw_data(); + PE_TEST(image->expand_section(s, s.get_virtual_address() + 0x100, 0x5000, pe_base::expand_section_virtual) == true, "Section expand test 5", test_level_normal); + PE_TEST(s.get_virtual_size() >= 0x100 + 0x5000, "Section expand test 6", test_level_normal); + PE_TEST(old_raw_size == s.get_size_of_raw_data(), "Section expand test 7", test_level_normal); + PE_TEST(image->expand_section(s, s.get_virtual_address() + 0x100, 0x1000, pe_base::expand_section_raw) == false, "Section expand test 8", test_level_normal); + PE_TEST(image->expand_section(s, s.get_virtual_address() + 0x100, 0x5000, pe_base::expand_section_virtual) == false, "Section expand test 9", test_level_normal); + } + + { + image->get_image_sections().pop_back(); + + std::stringstream new_pe(std::ios::in | std::ios::out | std::ios::binary); + PE_TEST_EXCEPTION(rebuild_pe(*image, new_pe, false, true, true), "Rebuild PE test 1", test_level_critical); + + std::auto_ptr<pe_base> new_image; + PE_TEST_EXCEPTION(new_image.reset(new pe_base(pe_factory::create_pe(new_pe))), "Creation, type detection and copying test 2", test_level_critical); + + section_list& sections = image->get_image_sections(); + section_list& new_sections = new_image->get_image_sections(); + PE_TEST(sections.size() == new_sections.size(), "Rebuild PE test 2", test_level_normal); + + for(uint32_t i = 0; i != sections.size(); ++i) + { + std::string raw_data_old(sections[i].get_raw_data()); + std::string raw_data_new(new_sections[i].get_raw_data()); + pe_utils::strip_nullbytes(raw_data_old); + pe_utils::strip_nullbytes(raw_data_new); + + std::cout << "Rebuilt PE test iteration " << i << std::endl; + PE_TEST(raw_data_old == raw_data_new, "Rebuild PE test (section raw data compare)", test_level_normal); + PE_TEST(sections[i].get_virtual_address() == new_sections[i].get_virtual_address(), "Rebuild PE test (section virtual addresses compare)", test_level_normal); + PE_TEST(sections[i].get_aligned_virtual_size(image->get_section_alignment()) == new_sections[i].get_aligned_virtual_size(new_image->get_section_alignment()), "Rebuild PE test (section virtual sizes compare)", test_level_normal); + } + + + new_pe.str(""); + PE_TEST_EXCEPTION(rebuild_pe(*image, new_pe, true, true, true), "Rebuild PE test 3", test_level_critical); + PE_TEST_EXCEPTION(new_image.reset(new pe_base(pe_factory::create_pe(new_pe))), "Creation, type detection and copying test 3", test_level_critical); + + image->set_stub_overlay("123"); + new_pe.str(""); + PE_TEST_EXCEPTION(rebuild_pe(*image, new_pe, false, true, true), "Rebuild PE test 4", test_level_critical); + PE_TEST_EXCEPTION(new_image.reset(new pe_base(pe_factory::create_pe(new_pe))), "Creation, type detection and copying test 4", test_level_critical); + new_pe.str(""); + PE_TEST_EXCEPTION(rebuild_pe(*image, new_pe, true, true, true), "Rebuild PE test 5", test_level_critical); + PE_TEST_EXCEPTION(new_image.reset(new pe_base(pe_factory::create_pe(new_pe))), "Creation, type detection and copying test 5", test_level_critical); + } + + + { + pe_base new_pe(pe_properties_32(), 0x1000, false, pe_win::image_subsystem_windows_cui); + PE_TEST(new_pe.get_section_alignment() == 0x1000, "Empty PE Creation test 1", test_level_normal); + PE_TEST(new_pe.get_subsystem() == pe_win::image_subsystem_windows_cui, "Empty PE Creation test 2", test_level_normal); + PE_TEST(!new_pe.check_characteristics_flag(pe_win::image_file_dll), "Empty PE Creation test 3", test_level_normal); + + std::stringstream new_pe_data(std::ios::in | std::ios::out | std::ios::binary); + PE_TEST_EXCEPTION(rebuild_pe(new_pe, new_pe_data, false, true), "Rebuild PE test 3", test_level_critical); + + std::auto_ptr<pe_base> new_pe_after_rebuild; + PE_TEST_EXCEPTION(new_pe_after_rebuild.reset(new pe_base(pe_factory::create_pe(new_pe_data))), "Creation, type detection and copying test 4", test_level_critical); + PE_TEST(new_pe_after_rebuild->get_section_alignment() == 0x1000, "Empty PE Read test 1", test_level_normal); + PE_TEST(new_pe_after_rebuild->get_number_of_sections() == 0, "Empty PE Read test 2", test_level_normal); + } + + PE_TEST_END + + return 0; +} diff --git a/tests/tests_basic/tests_basic.vcproj b/tests/tests_basic/tests_basic.vcproj new file mode 100644 index 0000000..910ba82 --- /dev/null +++ b/tests/tests_basic/tests_basic.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="tests_basic" + ProjectGUID="{3451AE03-3363-445B-8DA8-94B197563D59}" + RootNamespace="tests_basic" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/tests_basic/tests_basic.vcxproj b/tests/tests_basic/tests_basic.vcxproj new file mode 100644 index 0000000..5ec2275 --- /dev/null +++ b/tests/tests_basic/tests_basic.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{7870A9AC-92BB-423B-BC03-FBF7B46CD338}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>tests_basic</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/tests_basic/tests_basic.vcxproj.filters b/tests/tests_basic/tests_basic.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/tests_basic/tests_basic.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/tests/tests_utils/Makefile b/tests/tests_utils/Makefile new file mode 100644 index 0000000..422827f --- /dev/null +++ b/tests/tests_utils/Makefile @@ -0,0 +1 @@ +include ../tests.mak diff --git a/tests/tests_utils/main.cpp b/tests/tests_utils/main.cpp new file mode 100644 index 0000000..251770d --- /dev/null +++ b/tests/tests_utils/main.cpp @@ -0,0 +1,52 @@ +#include <iostream> +#include <fstream> +#include <pe_bliss.h> +#define PE_FILES_UNUSED +#include "test.h" +#ifdef PE_BLISS_WINDOWS +#include "lib.h" +#endif + +using namespace pe_bliss; + +int main(int argc, char* argv[]) +{ + PE_TEST_START + + const char data[] = "abcdefgh"; + PE_TEST(pe_utils::is_null_terminated(data, sizeof(data)), "is_null_terminated test 1", test_level_normal); + PE_TEST(!pe_utils::is_null_terminated(data, sizeof(data) - 1), "is_null_terminated test 2", test_level_normal); + + std::string str("test\0\0\0"); + PE_TEST_EXCEPTION(pe_utils::strip_nullbytes(str), "strip_nullbytes test 1", test_level_normal); + PE_TEST(str == "test", "strip_nullbytes test 2", test_level_normal); + + PE_TEST(pe_utils::is_power_of_2(8), "is_power_of_2 test 1", test_level_normal); + PE_TEST(!pe_utils::is_power_of_2(7), "is_power_of_2 test 2", test_level_normal); + + PE_TEST(pe_utils::align_down(99, 4) == 96, "align_down test 1", test_level_normal); + PE_TEST(pe_utils::align_down(100, 4) == 100, "align_down test 2", test_level_normal); + + PE_TEST(pe_utils::align_up(99, 4) == 100, "align_up test 1", test_level_normal); + PE_TEST(pe_utils::align_up(100, 4) == 100, "align_up test 2", test_level_normal); + + PE_TEST(pe_utils::is_sum_safe(100, 100), "is_sum_safe test 1", test_level_normal); + PE_TEST(!pe_utils::is_sum_safe(pe_utils::max_dword - 1, 2), "is_sum_safe test 2", test_level_normal); + + std::ifstream file(argv[0]); + file.seekg(0, std::ios::end); + std::streamoff size = file.tellg(); + file.seekg(123); + + PE_TEST(pe_utils::get_file_size(file) == size, "get_file_size test 1", test_level_normal); + PE_TEST(static_cast<std::streamoff>(file.tellg()) == static_cast<std::streamoff>(123), "get_file_size test 2", test_level_normal); //Restore position test + +#ifndef PE_BLISS_WINDOWS + PE_TEST(pe_utils::from_ucs2(pe_utils::to_ucs2(L"alala")) == L"alala", "to_ucs2 & from_ucs2 test 1", test_level_normal); + PE_TEST(pe_utils::from_ucs2(pe_utils::to_ucs2(L"")) == L"", "to_ucs2 & from_ucs2 test 2", test_level_normal); +#endif + + PE_TEST_END + + return 0; +} diff --git a/tests/tests_utils/tests_utils.vcproj b/tests/tests_utils/tests_utils.vcproj new file mode 100644 index 0000000..0c03e3c --- /dev/null +++ b/tests/tests_utils/tests_utils.vcproj @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="windows-1251"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="tests_utils" + ProjectGUID="{B0478C25-73AD-4085-BA1A-DDF66431EB6E}" + RootNamespace="tests_utils" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)" + IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../../pe_lib/;../" + PreprocessorDefinitions="_WIN64;NDEBUG;_CONSOLE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + CommandLine="copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\main.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\lib.h" + > + </File> + <File + RelativePath="..\test.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tests/tests_utils/tests_utils.vcxproj b/tests/tests_utils/tests_utils.vcxproj new file mode 100644 index 0000000..2589c6f --- /dev/null +++ b/tests/tests_utils/tests_utils.vcxproj @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{50212477-1614-49C9-9791-4AC72025DC76}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>tests_utils</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>../../pe_lib/;../</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + <PostBuildEvent> + <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\tests\bin\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h" /> + <ClInclude Include="..\test.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/tests/tests_utils/tests_utils.vcxproj.filters b/tests/tests_utils/tests_utils.vcxproj.filters new file mode 100644 index 0000000..ba7d6ae --- /dev/null +++ b/tests/tests_utils/tests_utils.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\lib.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\test.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file -- GitLab