From 4bcf1c120956613b5f899fb1d6f677961ea8806d Mon Sep 17 00:00:00 2001 From: "jln@chromium.org" Date: Fri, 13 Jul 2012 20:05:09 +0000 Subject: Move Windows sandbox - Move Windows sandbox to sandbox/win - Update sandbox_win.gypi git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146625 0039d316-1c4b-4281-b951-d872f2087c98 --- sandbox/sandbox.gyp | 2 +- sandbox/sandbox_poc/main_ui_window.cc | 668 ----------- sandbox/sandbox_poc/main_ui_window.h | 193 ---- sandbox/sandbox_poc/pocdll/exports.h | 89 -- sandbox/sandbox_poc/pocdll/fs.cc | 54 - sandbox/sandbox_poc/pocdll/handles.cc | 186 ---- sandbox/sandbox_poc/pocdll/invasive.cc | 196 ---- sandbox/sandbox_poc/pocdll/network.cc | 66 -- sandbox/sandbox_poc/pocdll/pocdll.cc | 27 - sandbox/sandbox_poc/pocdll/pocdll.vcproj | 218 ---- .../sandbox_poc/pocdll/processes_and_threads.cc | 102 -- sandbox/sandbox_poc/pocdll/registry.cc | 63 -- sandbox/sandbox_poc/pocdll/spyware.cc | 66 -- sandbox/sandbox_poc/pocdll/utils.h | 65 -- sandbox/sandbox_poc/resource.h | 30 - sandbox/sandbox_poc/sandbox.cc | 182 --- sandbox/sandbox_poc/sandbox.h | 10 - sandbox/sandbox_poc/sandbox.ico | Bin 1078 -> 0 bytes sandbox/sandbox_poc/sandbox.rc | 136 --- sandbox/sandbox_poc/sandbox_poc.vcproj | 202 ---- sandbox/sandbox_standalone.sln | 127 --- sandbox/sandbox_win.gypi | 342 ------ sandbox/src/Wow64.cc | 219 ---- sandbox/src/Wow64.h | 50 - sandbox/src/Wow64_64.cc | 18 - sandbox/src/acl.cc | 122 --- sandbox/src/acl.h | 41 - sandbox/src/broker_services.cc | 405 ------- sandbox/src/broker_services.h | 113 -- sandbox/src/crosscall_client.h | 483 -------- sandbox/src/crosscall_params.h | 293 ----- sandbox/src/crosscall_server.cc | 283 ----- sandbox/src/crosscall_server.h | 224 ---- sandbox/src/dep.cc | 89 -- sandbox/src/dep.h | 25 - sandbox/src/dep_test.cc | 158 --- sandbox/src/eat_resolver.cc | 89 -- sandbox/src/eat_resolver.h | 48 - sandbox/src/file_policy_test.cc | 598 ---------- sandbox/src/filesystem_dispatcher.cc | 297 ----- sandbox/src/filesystem_dispatcher.h | 57 - sandbox/src/filesystem_interception.cc | 351 ------ sandbox/src/filesystem_interception.h | 53 - sandbox/src/filesystem_policy.cc | 387 ------- sandbox/src/filesystem_policy.h | 107 -- sandbox/src/handle_closer.cc | 201 ---- sandbox/src/handle_closer.h | 75 -- sandbox/src/handle_closer_agent.cc | 145 --- sandbox/src/handle_closer_agent.h | 37 - sandbox/src/handle_closer_test.cc | 194 ---- sandbox/src/handle_dispatcher.cc | 90 -- sandbox/src/handle_dispatcher.h | 37 - sandbox/src/handle_interception.cc | 45 - sandbox/src/handle_interception.h | 24 - sandbox/src/handle_policy.cc | 94 -- sandbox/src/handle_policy.h | 41 - sandbox/src/handle_policy_test.cc | 114 -- sandbox/src/handle_table.cc | 181 --- sandbox/src/handle_table.h | 159 --- sandbox/src/integrity_level_test.cc | 90 -- sandbox/src/interception.cc | 551 ---------- sandbox/src/interception.h | 272 ----- sandbox/src/interception_agent.cc | 233 ---- sandbox/src/interception_agent.h | 87 -- sandbox/src/interception_internal.h | 76 -- sandbox/src/interception_unittest.cc | 212 ---- sandbox/src/interceptors.h | 54 - sandbox/src/interceptors_64.cc | 268 ----- sandbox/src/interceptors_64.h | 169 --- sandbox/src/internal_types.h | 75 -- sandbox/src/ipc_ping_test.cc | 58 - sandbox/src/ipc_tags.h | 37 - sandbox/src/ipc_unittest.cc | 641 ----------- sandbox/src/job.cc | 116 -- sandbox/src/job.h | 62 -- sandbox/src/job_unittest.cc | 189 ---- sandbox/src/named_pipe_dispatcher.cc | 66 -- sandbox/src/named_pipe_dispatcher.h | 37 - sandbox/src/named_pipe_interception.cc | 72 -- sandbox/src/named_pipe_interception.h | 36 - sandbox/src/named_pipe_policy.cc | 86 -- sandbox/src/named_pipe_policy.h | 45 - sandbox/src/named_pipe_policy_test.cc | 78 -- sandbox/src/nt_internals.h | 611 ----------- sandbox/src/policy_broker.cc | 118 -- sandbox/src/policy_broker.h | 23 - sandbox/src/policy_engine_opcodes.cc | 454 -------- sandbox/src/policy_engine_opcodes.h | 380 ------- sandbox/src/policy_engine_params.h | 202 ---- sandbox/src/policy_engine_processor.cc | 107 -- sandbox/src/policy_engine_processor.h | 145 --- sandbox/src/policy_engine_unittest.cc | 102 -- sandbox/src/policy_low_level.cc | 348 ------ sandbox/src/policy_low_level.h | 182 --- sandbox/src/policy_low_level_unittest.cc | 575 ---------- sandbox/src/policy_opcodes_unittest.cc | 344 ------ sandbox/src/policy_params.h | 64 -- sandbox/src/policy_target.cc | 127 --- sandbox/src/policy_target.h | 45 - sandbox/src/policy_target_test.cc | 337 ------ sandbox/src/process_policy_test.cc | 295 ----- sandbox/src/process_thread_dispatcher.cc | 245 ----- sandbox/src/process_thread_dispatcher.h | 47 - sandbox/src/process_thread_interception.cc | 447 -------- sandbox/src/process_thread_interception.h | 101 -- sandbox/src/process_thread_policy.cc | 242 ---- sandbox/src/process_thread_policy.h | 82 -- sandbox/src/registry_dispatcher.cc | 161 --- sandbox/src/registry_dispatcher.h | 39 - sandbox/src/registry_interception.cc | 176 --- sandbox/src/registry_interception.h | 38 - sandbox/src/registry_policy.cc | 227 ---- sandbox/src/registry_policy.h | 57 - sandbox/src/registry_policy_test.cc | 289 ----- sandbox/src/resolver.cc | 62 -- sandbox/src/resolver.h | 105 -- sandbox/src/resolver_32.cc | 88 -- sandbox/src/resolver_64.cc | 69 -- sandbox/src/restricted_token.cc | 466 -------- sandbox/src/restricted_token.h | 198 ---- sandbox/src/restricted_token_unittest.cc | 530 --------- sandbox/src/restricted_token_utils.cc | 344 ------ sandbox/src/restricted_token_utils.h | 83 -- sandbox/src/sandbox.cc | 51 - sandbox/src/sandbox.h | 156 --- sandbox/src/sandbox.vcproj | 658 ----------- sandbox/src/sandbox_factory.h | 50 - sandbox/src/sandbox_nt_types.h | 46 - sandbox/src/sandbox_nt_util.cc | 599 ---------- sandbox/src/sandbox_nt_util.h | 173 --- sandbox/src/sandbox_policy.h | 190 ---- sandbox/src/sandbox_policy_base.cc | 542 --------- sandbox/src/sandbox_policy_base.h | 139 --- sandbox/src/sandbox_types.h | 81 -- sandbox/src/sandbox_utils.cc | 79 -- sandbox/src/sandbox_utils.h | 38 - sandbox/src/security_level.h | 127 --- sandbox/src/service_resolver.cc | 42 - sandbox/src/service_resolver.h | 148 --- sandbox/src/service_resolver_32.cc | 424 ------- sandbox/src/service_resolver_64.cc | 193 ---- sandbox/src/service_resolver_unittest.cc | 227 ---- sandbox/src/shared_handles.cc | 67 -- sandbox/src/shared_handles.h | 108 -- sandbox/src/sharedmem_ipc_client.cc | 152 --- sandbox/src/sharedmem_ipc_client.h | 136 --- sandbox/src/sharedmem_ipc_server.cc | 410 ------- sandbox/src/sharedmem_ipc_server.h | 127 --- sandbox/src/sid.cc | 26 - sandbox/src/sid.h | 29 - sandbox/src/sid_unittest.cc | 71 -- sandbox/src/sidestep/ia32_modrm_map.cpp | 92 -- sandbox/src/sidestep/ia32_opcode_map.cpp | 1159 -------------------- sandbox/src/sidestep/mini_disassembler.cpp | 395 ------- sandbox/src/sidestep/mini_disassembler.h | 156 --- sandbox/src/sidestep/mini_disassembler_types.h | 197 ---- sandbox/src/sidestep/preamble_patcher.h | 109 -- .../src/sidestep/preamble_patcher_with_stub.cpp | 179 --- sandbox/src/sidestep_resolver.cc | 200 ---- sandbox/src/sidestep_resolver.h | 73 -- sandbox/src/sync_dispatcher.cc | 86 -- sandbox/src/sync_dispatcher.h | 38 - sandbox/src/sync_interception.cc | 107 -- sandbox/src/sync_interception.h | 41 - sandbox/src/sync_policy.cc | 114 -- sandbox/src/sync_policy.h | 51 - sandbox/src/sync_policy_test.cc | 145 --- sandbox/src/target_interceptions.cc | 100 -- sandbox/src/target_interceptions.h | 35 - sandbox/src/target_process.cc | 355 ------ sandbox/src/target_process.h | 122 --- sandbox/src/target_services.cc | 188 ---- sandbox/src/target_services.h | 71 -- sandbox/src/threadpool_unittest.cc | 94 -- sandbox/src/unload_dll_test.cc | 96 -- sandbox/src/win2k_threadpool.cc | 60 - sandbox/src/win2k_threadpool.h | 58 - sandbox/src/win_utils.cc | 323 ------ sandbox/src/win_utils.h | 110 -- sandbox/src/win_utils_unittest.cc | 82 -- sandbox/src/window.cc | 142 --- sandbox/src/window.h | 39 - sandbox/tests/common/controller.cc | 338 ------ sandbox/tests/common/controller.h | 145 --- sandbox/tests/common/test_utils.cc | 72 -- sandbox/tests/common/test_utils.h | 19 - .../tests/integration_tests/integration_tests.cc | 18 - .../integration_tests/integration_tests_test.cc | 94 -- .../sbox_integration_tests.vcproj | 242 ---- sandbox/tests/unit_tests/sbox_unittests.vcproj | 258 ----- sandbox/tests/unit_tests/unit_tests.cc | 16 - sandbox/tests/validation_tests/commands.cc | 262 ----- sandbox/tests/validation_tests/commands.h | 37 - .../validation_tests/sbox_validation_tests.vcproj | 216 ---- sandbox/tests/validation_tests/suite.cc | 200 ---- sandbox/tests/validation_tests/unit_tests.cc | 16 - sandbox/tools/finder/finder.cc | 64 -- sandbox/tools/finder/finder.h | 143 --- sandbox/tools/finder/finder.vcproj | 201 ---- sandbox/tools/finder/finder_fs.cc | 117 -- sandbox/tools/finder/finder_kernel.cc | 248 ----- sandbox/tools/finder/finder_registry.cc | 93 -- sandbox/tools/finder/main.cc | 147 --- sandbox/tools/finder/ntundoc.h | 275 ----- sandbox/tools/launcher/launcher.cc | 156 --- sandbox/tools/launcher/launcher.vcproj | 177 --- sandbox/win/sandbox_poc/main_ui_window.cc | 668 +++++++++++ sandbox/win/sandbox_poc/main_ui_window.h | 193 ++++ sandbox/win/sandbox_poc/pocdll/exports.h | 89 ++ sandbox/win/sandbox_poc/pocdll/fs.cc | 54 + sandbox/win/sandbox_poc/pocdll/handles.cc | 186 ++++ sandbox/win/sandbox_poc/pocdll/invasive.cc | 196 ++++ sandbox/win/sandbox_poc/pocdll/network.cc | 66 ++ sandbox/win/sandbox_poc/pocdll/pocdll.cc | 27 + sandbox/win/sandbox_poc/pocdll/pocdll.vcproj | 218 ++++ .../sandbox_poc/pocdll/processes_and_threads.cc | 102 ++ sandbox/win/sandbox_poc/pocdll/registry.cc | 63 ++ sandbox/win/sandbox_poc/pocdll/spyware.cc | 66 ++ sandbox/win/sandbox_poc/pocdll/utils.h | 65 ++ sandbox/win/sandbox_poc/resource.h | 30 + sandbox/win/sandbox_poc/sandbox.cc | 182 +++ sandbox/win/sandbox_poc/sandbox.h | 10 + sandbox/win/sandbox_poc/sandbox.ico | Bin 0 -> 1078 bytes sandbox/win/sandbox_poc/sandbox.rc | 136 +++ sandbox/win/sandbox_poc/sandbox_poc.vcproj | 202 ++++ sandbox/win/sandbox_standalone.sln | 127 +++ sandbox/win/sandbox_win.gypi | 342 ++++++ sandbox/win/src/Wow64.cc | 219 ++++ sandbox/win/src/Wow64.h | 50 + sandbox/win/src/Wow64_64.cc | 18 + sandbox/win/src/acl.cc | 122 +++ sandbox/win/src/acl.h | 41 + sandbox/win/src/broker_services.cc | 405 +++++++ sandbox/win/src/broker_services.h | 113 ++ sandbox/win/src/crosscall_client.h | 483 ++++++++ sandbox/win/src/crosscall_params.h | 293 +++++ sandbox/win/src/crosscall_server.cc | 283 +++++ sandbox/win/src/crosscall_server.h | 224 ++++ sandbox/win/src/dep.cc | 89 ++ sandbox/win/src/dep.h | 25 + sandbox/win/src/dep_test.cc | 158 +++ sandbox/win/src/eat_resolver.cc | 89 ++ sandbox/win/src/eat_resolver.h | 48 + sandbox/win/src/file_policy_test.cc | 598 ++++++++++ sandbox/win/src/filesystem_dispatcher.cc | 297 +++++ sandbox/win/src/filesystem_dispatcher.h | 57 + sandbox/win/src/filesystem_interception.cc | 351 ++++++ sandbox/win/src/filesystem_interception.h | 53 + sandbox/win/src/filesystem_policy.cc | 387 +++++++ sandbox/win/src/filesystem_policy.h | 107 ++ sandbox/win/src/handle_closer.cc | 201 ++++ sandbox/win/src/handle_closer.h | 75 ++ sandbox/win/src/handle_closer_agent.cc | 145 +++ sandbox/win/src/handle_closer_agent.h | 37 + sandbox/win/src/handle_closer_test.cc | 194 ++++ sandbox/win/src/handle_dispatcher.cc | 90 ++ sandbox/win/src/handle_dispatcher.h | 37 + sandbox/win/src/handle_interception.cc | 45 + sandbox/win/src/handle_interception.h | 24 + sandbox/win/src/handle_policy.cc | 94 ++ sandbox/win/src/handle_policy.h | 41 + sandbox/win/src/handle_policy_test.cc | 114 ++ sandbox/win/src/handle_table.cc | 181 +++ sandbox/win/src/handle_table.h | 159 +++ sandbox/win/src/integrity_level_test.cc | 90 ++ sandbox/win/src/interception.cc | 551 ++++++++++ sandbox/win/src/interception.h | 272 +++++ sandbox/win/src/interception_agent.cc | 233 ++++ sandbox/win/src/interception_agent.h | 87 ++ sandbox/win/src/interception_internal.h | 76 ++ sandbox/win/src/interception_unittest.cc | 212 ++++ sandbox/win/src/interceptors.h | 54 + sandbox/win/src/interceptors_64.cc | 268 +++++ sandbox/win/src/interceptors_64.h | 169 +++ sandbox/win/src/internal_types.h | 75 ++ sandbox/win/src/ipc_ping_test.cc | 58 + sandbox/win/src/ipc_tags.h | 37 + sandbox/win/src/ipc_unittest.cc | 641 +++++++++++ sandbox/win/src/job.cc | 116 ++ sandbox/win/src/job.h | 62 ++ sandbox/win/src/job_unittest.cc | 189 ++++ sandbox/win/src/named_pipe_dispatcher.cc | 66 ++ sandbox/win/src/named_pipe_dispatcher.h | 37 + sandbox/win/src/named_pipe_interception.cc | 72 ++ sandbox/win/src/named_pipe_interception.h | 36 + sandbox/win/src/named_pipe_policy.cc | 86 ++ sandbox/win/src/named_pipe_policy.h | 45 + sandbox/win/src/named_pipe_policy_test.cc | 78 ++ sandbox/win/src/nt_internals.h | 611 +++++++++++ sandbox/win/src/policy_broker.cc | 118 ++ sandbox/win/src/policy_broker.h | 23 + sandbox/win/src/policy_engine_opcodes.cc | 454 ++++++++ sandbox/win/src/policy_engine_opcodes.h | 380 +++++++ sandbox/win/src/policy_engine_params.h | 202 ++++ sandbox/win/src/policy_engine_processor.cc | 107 ++ sandbox/win/src/policy_engine_processor.h | 145 +++ sandbox/win/src/policy_engine_unittest.cc | 102 ++ sandbox/win/src/policy_low_level.cc | 348 ++++++ sandbox/win/src/policy_low_level.h | 182 +++ sandbox/win/src/policy_low_level_unittest.cc | 575 ++++++++++ sandbox/win/src/policy_opcodes_unittest.cc | 344 ++++++ sandbox/win/src/policy_params.h | 64 ++ sandbox/win/src/policy_target.cc | 127 +++ sandbox/win/src/policy_target.h | 45 + sandbox/win/src/policy_target_test.cc | 337 ++++++ sandbox/win/src/process_policy_test.cc | 295 +++++ sandbox/win/src/process_thread_dispatcher.cc | 245 +++++ sandbox/win/src/process_thread_dispatcher.h | 47 + sandbox/win/src/process_thread_interception.cc | 447 ++++++++ sandbox/win/src/process_thread_interception.h | 101 ++ sandbox/win/src/process_thread_policy.cc | 242 ++++ sandbox/win/src/process_thread_policy.h | 82 ++ sandbox/win/src/registry_dispatcher.cc | 161 +++ sandbox/win/src/registry_dispatcher.h | 39 + sandbox/win/src/registry_interception.cc | 176 +++ sandbox/win/src/registry_interception.h | 38 + sandbox/win/src/registry_policy.cc | 227 ++++ sandbox/win/src/registry_policy.h | 57 + sandbox/win/src/registry_policy_test.cc | 289 +++++ sandbox/win/src/resolver.cc | 62 ++ sandbox/win/src/resolver.h | 105 ++ sandbox/win/src/resolver_32.cc | 88 ++ sandbox/win/src/resolver_64.cc | 69 ++ sandbox/win/src/restricted_token.cc | 466 ++++++++ sandbox/win/src/restricted_token.h | 198 ++++ sandbox/win/src/restricted_token_unittest.cc | 530 +++++++++ sandbox/win/src/restricted_token_utils.cc | 344 ++++++ sandbox/win/src/restricted_token_utils.h | 83 ++ sandbox/win/src/sandbox.cc | 51 + sandbox/win/src/sandbox.h | 156 +++ sandbox/win/src/sandbox.vcproj | 658 +++++++++++ sandbox/win/src/sandbox_factory.h | 50 + sandbox/win/src/sandbox_nt_types.h | 46 + sandbox/win/src/sandbox_nt_util.cc | 599 ++++++++++ sandbox/win/src/sandbox_nt_util.h | 173 +++ sandbox/win/src/sandbox_policy.h | 190 ++++ sandbox/win/src/sandbox_policy_base.cc | 542 +++++++++ sandbox/win/src/sandbox_policy_base.h | 139 +++ sandbox/win/src/sandbox_types.h | 81 ++ sandbox/win/src/sandbox_utils.cc | 79 ++ sandbox/win/src/sandbox_utils.h | 38 + sandbox/win/src/security_level.h | 127 +++ sandbox/win/src/service_resolver.cc | 42 + sandbox/win/src/service_resolver.h | 148 +++ sandbox/win/src/service_resolver_32.cc | 424 +++++++ sandbox/win/src/service_resolver_64.cc | 193 ++++ sandbox/win/src/service_resolver_unittest.cc | 227 ++++ sandbox/win/src/shared_handles.cc | 67 ++ sandbox/win/src/shared_handles.h | 108 ++ sandbox/win/src/sharedmem_ipc_client.cc | 152 +++ sandbox/win/src/sharedmem_ipc_client.h | 136 +++ sandbox/win/src/sharedmem_ipc_server.cc | 410 +++++++ sandbox/win/src/sharedmem_ipc_server.h | 127 +++ sandbox/win/src/sid.cc | 26 + sandbox/win/src/sid.h | 29 + sandbox/win/src/sid_unittest.cc | 71 ++ sandbox/win/src/sidestep/ia32_modrm_map.cpp | 92 ++ sandbox/win/src/sidestep/ia32_opcode_map.cpp | 1159 ++++++++++++++++++++ sandbox/win/src/sidestep/mini_disassembler.cpp | 395 +++++++ sandbox/win/src/sidestep/mini_disassembler.h | 156 +++ sandbox/win/src/sidestep/mini_disassembler_types.h | 197 ++++ sandbox/win/src/sidestep/preamble_patcher.h | 109 ++ .../src/sidestep/preamble_patcher_with_stub.cpp | 179 +++ sandbox/win/src/sidestep_resolver.cc | 200 ++++ sandbox/win/src/sidestep_resolver.h | 73 ++ sandbox/win/src/sync_dispatcher.cc | 86 ++ sandbox/win/src/sync_dispatcher.h | 38 + sandbox/win/src/sync_interception.cc | 107 ++ sandbox/win/src/sync_interception.h | 41 + sandbox/win/src/sync_policy.cc | 114 ++ sandbox/win/src/sync_policy.h | 51 + sandbox/win/src/sync_policy_test.cc | 145 +++ sandbox/win/src/target_interceptions.cc | 100 ++ sandbox/win/src/target_interceptions.h | 35 + sandbox/win/src/target_process.cc | 355 ++++++ sandbox/win/src/target_process.h | 122 +++ sandbox/win/src/target_services.cc | 188 ++++ sandbox/win/src/target_services.h | 71 ++ sandbox/win/src/threadpool_unittest.cc | 94 ++ sandbox/win/src/unload_dll_test.cc | 96 ++ sandbox/win/src/win2k_threadpool.cc | 60 + sandbox/win/src/win2k_threadpool.h | 58 + sandbox/win/src/win_utils.cc | 323 ++++++ sandbox/win/src/win_utils.h | 110 ++ sandbox/win/src/win_utils_unittest.cc | 82 ++ sandbox/win/src/window.cc | 142 +++ sandbox/win/src/window.h | 39 + sandbox/win/tests/common/controller.cc | 338 ++++++ sandbox/win/tests/common/controller.h | 145 +++ sandbox/win/tests/common/test_utils.cc | 72 ++ sandbox/win/tests/common/test_utils.h | 19 + .../tests/integration_tests/integration_tests.cc | 18 + .../integration_tests/integration_tests_test.cc | 94 ++ .../sbox_integration_tests.vcproj | 242 ++++ sandbox/win/tests/unit_tests/sbox_unittests.vcproj | 258 +++++ sandbox/win/tests/unit_tests/unit_tests.cc | 16 + sandbox/win/tests/validation_tests/commands.cc | 262 +++++ sandbox/win/tests/validation_tests/commands.h | 37 + .../validation_tests/sbox_validation_tests.vcproj | 216 ++++ sandbox/win/tests/validation_tests/suite.cc | 200 ++++ sandbox/win/tests/validation_tests/unit_tests.cc | 16 + sandbox/win/tools/finder/finder.cc | 64 ++ sandbox/win/tools/finder/finder.h | 143 +++ sandbox/win/tools/finder/finder.vcproj | 201 ++++ sandbox/win/tools/finder/finder_fs.cc | 117 ++ sandbox/win/tools/finder/finder_kernel.cc | 248 +++++ sandbox/win/tools/finder/finder_registry.cc | 93 ++ sandbox/win/tools/finder/main.cc | 147 +++ sandbox/win/tools/finder/ntundoc.h | 275 +++++ sandbox/win/tools/launcher/launcher.cc | 156 +++ sandbox/win/tools/launcher/launcher.vcproj | 177 +++ sandbox/win/wow_helper.sln | 19 + sandbox/win/wow_helper/service64_resolver.cc | 342 ++++++ sandbox/win/wow_helper/service64_resolver.h | 72 ++ sandbox/win/wow_helper/target_code.cc | 34 + sandbox/win/wow_helper/target_code.h | 41 + sandbox/win/wow_helper/wow_helper.cc | 123 +++ sandbox/win/wow_helper/wow_helper.exe | Bin 0 -> 67072 bytes sandbox/win/wow_helper/wow_helper.pdb | Bin 0 -> 699392 bytes sandbox/win/wow_helper/wow_helper.vcproj | 223 ++++ sandbox/wow_helper.sln | 19 - sandbox/wow_helper/service64_resolver.cc | 342 ------ sandbox/wow_helper/service64_resolver.h | 72 -- sandbox/wow_helper/target_code.cc | 34 - sandbox/wow_helper/target_code.h | 41 - sandbox/wow_helper/wow_helper.cc | 123 --- sandbox/wow_helper/wow_helper.exe | Bin 67072 -> 0 bytes sandbox/wow_helper/wow_helper.pdb | Bin 699392 -> 0 bytes sandbox/wow_helper/wow_helper.vcproj | 223 ---- 429 files changed, 35649 insertions(+), 35649 deletions(-) delete mode 100644 sandbox/sandbox_poc/main_ui_window.cc delete mode 100644 sandbox/sandbox_poc/main_ui_window.h delete mode 100644 sandbox/sandbox_poc/pocdll/exports.h delete mode 100644 sandbox/sandbox_poc/pocdll/fs.cc delete mode 100644 sandbox/sandbox_poc/pocdll/handles.cc delete mode 100644 sandbox/sandbox_poc/pocdll/invasive.cc delete mode 100644 sandbox/sandbox_poc/pocdll/network.cc delete mode 100644 sandbox/sandbox_poc/pocdll/pocdll.cc delete mode 100644 sandbox/sandbox_poc/pocdll/pocdll.vcproj delete mode 100644 sandbox/sandbox_poc/pocdll/processes_and_threads.cc delete mode 100644 sandbox/sandbox_poc/pocdll/registry.cc delete mode 100644 sandbox/sandbox_poc/pocdll/spyware.cc delete mode 100644 sandbox/sandbox_poc/pocdll/utils.h delete mode 100644 sandbox/sandbox_poc/resource.h delete mode 100644 sandbox/sandbox_poc/sandbox.cc delete mode 100644 sandbox/sandbox_poc/sandbox.h delete mode 100644 sandbox/sandbox_poc/sandbox.ico delete mode 100644 sandbox/sandbox_poc/sandbox.rc delete mode 100644 sandbox/sandbox_poc/sandbox_poc.vcproj delete mode 100644 sandbox/sandbox_standalone.sln delete mode 100644 sandbox/sandbox_win.gypi delete mode 100644 sandbox/src/Wow64.cc delete mode 100644 sandbox/src/Wow64.h delete mode 100644 sandbox/src/Wow64_64.cc delete mode 100644 sandbox/src/acl.cc delete mode 100644 sandbox/src/acl.h delete mode 100644 sandbox/src/broker_services.cc delete mode 100644 sandbox/src/broker_services.h delete mode 100644 sandbox/src/crosscall_client.h delete mode 100644 sandbox/src/crosscall_params.h delete mode 100644 sandbox/src/crosscall_server.cc delete mode 100644 sandbox/src/crosscall_server.h delete mode 100644 sandbox/src/dep.cc delete mode 100644 sandbox/src/dep.h delete mode 100644 sandbox/src/dep_test.cc delete mode 100644 sandbox/src/eat_resolver.cc delete mode 100644 sandbox/src/eat_resolver.h delete mode 100644 sandbox/src/file_policy_test.cc delete mode 100644 sandbox/src/filesystem_dispatcher.cc delete mode 100644 sandbox/src/filesystem_dispatcher.h delete mode 100644 sandbox/src/filesystem_interception.cc delete mode 100644 sandbox/src/filesystem_interception.h delete mode 100644 sandbox/src/filesystem_policy.cc delete mode 100644 sandbox/src/filesystem_policy.h delete mode 100644 sandbox/src/handle_closer.cc delete mode 100644 sandbox/src/handle_closer.h delete mode 100644 sandbox/src/handle_closer_agent.cc delete mode 100644 sandbox/src/handle_closer_agent.h delete mode 100644 sandbox/src/handle_closer_test.cc delete mode 100644 sandbox/src/handle_dispatcher.cc delete mode 100644 sandbox/src/handle_dispatcher.h delete mode 100644 sandbox/src/handle_interception.cc delete mode 100644 sandbox/src/handle_interception.h delete mode 100644 sandbox/src/handle_policy.cc delete mode 100644 sandbox/src/handle_policy.h delete mode 100644 sandbox/src/handle_policy_test.cc delete mode 100644 sandbox/src/handle_table.cc delete mode 100644 sandbox/src/handle_table.h delete mode 100644 sandbox/src/integrity_level_test.cc delete mode 100644 sandbox/src/interception.cc delete mode 100644 sandbox/src/interception.h delete mode 100644 sandbox/src/interception_agent.cc delete mode 100644 sandbox/src/interception_agent.h delete mode 100644 sandbox/src/interception_internal.h delete mode 100644 sandbox/src/interception_unittest.cc delete mode 100644 sandbox/src/interceptors.h delete mode 100644 sandbox/src/interceptors_64.cc delete mode 100644 sandbox/src/interceptors_64.h delete mode 100644 sandbox/src/internal_types.h delete mode 100644 sandbox/src/ipc_ping_test.cc delete mode 100644 sandbox/src/ipc_tags.h delete mode 100644 sandbox/src/ipc_unittest.cc delete mode 100644 sandbox/src/job.cc delete mode 100644 sandbox/src/job.h delete mode 100644 sandbox/src/job_unittest.cc delete mode 100644 sandbox/src/named_pipe_dispatcher.cc delete mode 100644 sandbox/src/named_pipe_dispatcher.h delete mode 100644 sandbox/src/named_pipe_interception.cc delete mode 100644 sandbox/src/named_pipe_interception.h delete mode 100644 sandbox/src/named_pipe_policy.cc delete mode 100644 sandbox/src/named_pipe_policy.h delete mode 100644 sandbox/src/named_pipe_policy_test.cc delete mode 100644 sandbox/src/nt_internals.h delete mode 100644 sandbox/src/policy_broker.cc delete mode 100644 sandbox/src/policy_broker.h delete mode 100644 sandbox/src/policy_engine_opcodes.cc delete mode 100644 sandbox/src/policy_engine_opcodes.h delete mode 100644 sandbox/src/policy_engine_params.h delete mode 100644 sandbox/src/policy_engine_processor.cc delete mode 100644 sandbox/src/policy_engine_processor.h delete mode 100644 sandbox/src/policy_engine_unittest.cc delete mode 100644 sandbox/src/policy_low_level.cc delete mode 100644 sandbox/src/policy_low_level.h delete mode 100644 sandbox/src/policy_low_level_unittest.cc delete mode 100644 sandbox/src/policy_opcodes_unittest.cc delete mode 100644 sandbox/src/policy_params.h delete mode 100644 sandbox/src/policy_target.cc delete mode 100644 sandbox/src/policy_target.h delete mode 100644 sandbox/src/policy_target_test.cc delete mode 100644 sandbox/src/process_policy_test.cc delete mode 100644 sandbox/src/process_thread_dispatcher.cc delete mode 100644 sandbox/src/process_thread_dispatcher.h delete mode 100644 sandbox/src/process_thread_interception.cc delete mode 100644 sandbox/src/process_thread_interception.h delete mode 100644 sandbox/src/process_thread_policy.cc delete mode 100644 sandbox/src/process_thread_policy.h delete mode 100644 sandbox/src/registry_dispatcher.cc delete mode 100644 sandbox/src/registry_dispatcher.h delete mode 100644 sandbox/src/registry_interception.cc delete mode 100644 sandbox/src/registry_interception.h delete mode 100644 sandbox/src/registry_policy.cc delete mode 100644 sandbox/src/registry_policy.h delete mode 100644 sandbox/src/registry_policy_test.cc delete mode 100644 sandbox/src/resolver.cc delete mode 100644 sandbox/src/resolver.h delete mode 100644 sandbox/src/resolver_32.cc delete mode 100644 sandbox/src/resolver_64.cc delete mode 100644 sandbox/src/restricted_token.cc delete mode 100644 sandbox/src/restricted_token.h delete mode 100644 sandbox/src/restricted_token_unittest.cc delete mode 100644 sandbox/src/restricted_token_utils.cc delete mode 100644 sandbox/src/restricted_token_utils.h delete mode 100644 sandbox/src/sandbox.cc delete mode 100644 sandbox/src/sandbox.h delete mode 100644 sandbox/src/sandbox.vcproj delete mode 100644 sandbox/src/sandbox_factory.h delete mode 100644 sandbox/src/sandbox_nt_types.h delete mode 100644 sandbox/src/sandbox_nt_util.cc delete mode 100644 sandbox/src/sandbox_nt_util.h delete mode 100644 sandbox/src/sandbox_policy.h delete mode 100644 sandbox/src/sandbox_policy_base.cc delete mode 100644 sandbox/src/sandbox_policy_base.h delete mode 100644 sandbox/src/sandbox_types.h delete mode 100644 sandbox/src/sandbox_utils.cc delete mode 100644 sandbox/src/sandbox_utils.h delete mode 100644 sandbox/src/security_level.h delete mode 100644 sandbox/src/service_resolver.cc delete mode 100644 sandbox/src/service_resolver.h delete mode 100644 sandbox/src/service_resolver_32.cc delete mode 100644 sandbox/src/service_resolver_64.cc delete mode 100644 sandbox/src/service_resolver_unittest.cc delete mode 100644 sandbox/src/shared_handles.cc delete mode 100644 sandbox/src/shared_handles.h delete mode 100644 sandbox/src/sharedmem_ipc_client.cc delete mode 100644 sandbox/src/sharedmem_ipc_client.h delete mode 100644 sandbox/src/sharedmem_ipc_server.cc delete mode 100644 sandbox/src/sharedmem_ipc_server.h delete mode 100644 sandbox/src/sid.cc delete mode 100644 sandbox/src/sid.h delete mode 100644 sandbox/src/sid_unittest.cc delete mode 100644 sandbox/src/sidestep/ia32_modrm_map.cpp delete mode 100644 sandbox/src/sidestep/ia32_opcode_map.cpp delete mode 100644 sandbox/src/sidestep/mini_disassembler.cpp delete mode 100644 sandbox/src/sidestep/mini_disassembler.h delete mode 100644 sandbox/src/sidestep/mini_disassembler_types.h delete mode 100644 sandbox/src/sidestep/preamble_patcher.h delete mode 100644 sandbox/src/sidestep/preamble_patcher_with_stub.cpp delete mode 100644 sandbox/src/sidestep_resolver.cc delete mode 100644 sandbox/src/sidestep_resolver.h delete mode 100644 sandbox/src/sync_dispatcher.cc delete mode 100644 sandbox/src/sync_dispatcher.h delete mode 100644 sandbox/src/sync_interception.cc delete mode 100644 sandbox/src/sync_interception.h delete mode 100644 sandbox/src/sync_policy.cc delete mode 100644 sandbox/src/sync_policy.h delete mode 100644 sandbox/src/sync_policy_test.cc delete mode 100644 sandbox/src/target_interceptions.cc delete mode 100644 sandbox/src/target_interceptions.h delete mode 100644 sandbox/src/target_process.cc delete mode 100644 sandbox/src/target_process.h delete mode 100644 sandbox/src/target_services.cc delete mode 100644 sandbox/src/target_services.h delete mode 100644 sandbox/src/threadpool_unittest.cc delete mode 100644 sandbox/src/unload_dll_test.cc delete mode 100644 sandbox/src/win2k_threadpool.cc delete mode 100644 sandbox/src/win2k_threadpool.h delete mode 100644 sandbox/src/win_utils.cc delete mode 100644 sandbox/src/win_utils.h delete mode 100644 sandbox/src/win_utils_unittest.cc delete mode 100644 sandbox/src/window.cc delete mode 100644 sandbox/src/window.h delete mode 100644 sandbox/tests/common/controller.cc delete mode 100644 sandbox/tests/common/controller.h delete mode 100644 sandbox/tests/common/test_utils.cc delete mode 100644 sandbox/tests/common/test_utils.h delete mode 100644 sandbox/tests/integration_tests/integration_tests.cc delete mode 100644 sandbox/tests/integration_tests/integration_tests_test.cc delete mode 100644 sandbox/tests/integration_tests/sbox_integration_tests.vcproj delete mode 100644 sandbox/tests/unit_tests/sbox_unittests.vcproj delete mode 100644 sandbox/tests/unit_tests/unit_tests.cc delete mode 100644 sandbox/tests/validation_tests/commands.cc delete mode 100644 sandbox/tests/validation_tests/commands.h delete mode 100644 sandbox/tests/validation_tests/sbox_validation_tests.vcproj delete mode 100644 sandbox/tests/validation_tests/suite.cc delete mode 100644 sandbox/tests/validation_tests/unit_tests.cc delete mode 100644 sandbox/tools/finder/finder.cc delete mode 100644 sandbox/tools/finder/finder.h delete mode 100644 sandbox/tools/finder/finder.vcproj delete mode 100644 sandbox/tools/finder/finder_fs.cc delete mode 100644 sandbox/tools/finder/finder_kernel.cc delete mode 100644 sandbox/tools/finder/finder_registry.cc delete mode 100644 sandbox/tools/finder/main.cc delete mode 100644 sandbox/tools/finder/ntundoc.h delete mode 100644 sandbox/tools/launcher/launcher.cc delete mode 100644 sandbox/tools/launcher/launcher.vcproj create mode 100644 sandbox/win/sandbox_poc/main_ui_window.cc create mode 100644 sandbox/win/sandbox_poc/main_ui_window.h create mode 100644 sandbox/win/sandbox_poc/pocdll/exports.h create mode 100644 sandbox/win/sandbox_poc/pocdll/fs.cc create mode 100644 sandbox/win/sandbox_poc/pocdll/handles.cc create mode 100644 sandbox/win/sandbox_poc/pocdll/invasive.cc create mode 100644 sandbox/win/sandbox_poc/pocdll/network.cc create mode 100644 sandbox/win/sandbox_poc/pocdll/pocdll.cc create mode 100644 sandbox/win/sandbox_poc/pocdll/pocdll.vcproj create mode 100644 sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc create mode 100644 sandbox/win/sandbox_poc/pocdll/registry.cc create mode 100644 sandbox/win/sandbox_poc/pocdll/spyware.cc create mode 100644 sandbox/win/sandbox_poc/pocdll/utils.h create mode 100644 sandbox/win/sandbox_poc/resource.h create mode 100644 sandbox/win/sandbox_poc/sandbox.cc create mode 100644 sandbox/win/sandbox_poc/sandbox.h create mode 100644 sandbox/win/sandbox_poc/sandbox.ico create mode 100644 sandbox/win/sandbox_poc/sandbox.rc create mode 100644 sandbox/win/sandbox_poc/sandbox_poc.vcproj create mode 100644 sandbox/win/sandbox_standalone.sln create mode 100644 sandbox/win/sandbox_win.gypi create mode 100644 sandbox/win/src/Wow64.cc create mode 100644 sandbox/win/src/Wow64.h create mode 100644 sandbox/win/src/Wow64_64.cc create mode 100644 sandbox/win/src/acl.cc create mode 100644 sandbox/win/src/acl.h create mode 100644 sandbox/win/src/broker_services.cc create mode 100644 sandbox/win/src/broker_services.h create mode 100644 sandbox/win/src/crosscall_client.h create mode 100644 sandbox/win/src/crosscall_params.h create mode 100644 sandbox/win/src/crosscall_server.cc create mode 100644 sandbox/win/src/crosscall_server.h create mode 100644 sandbox/win/src/dep.cc create mode 100644 sandbox/win/src/dep.h create mode 100644 sandbox/win/src/dep_test.cc create mode 100644 sandbox/win/src/eat_resolver.cc create mode 100644 sandbox/win/src/eat_resolver.h create mode 100644 sandbox/win/src/file_policy_test.cc create mode 100644 sandbox/win/src/filesystem_dispatcher.cc create mode 100644 sandbox/win/src/filesystem_dispatcher.h create mode 100644 sandbox/win/src/filesystem_interception.cc create mode 100644 sandbox/win/src/filesystem_interception.h create mode 100644 sandbox/win/src/filesystem_policy.cc create mode 100644 sandbox/win/src/filesystem_policy.h create mode 100644 sandbox/win/src/handle_closer.cc create mode 100644 sandbox/win/src/handle_closer.h create mode 100644 sandbox/win/src/handle_closer_agent.cc create mode 100644 sandbox/win/src/handle_closer_agent.h create mode 100644 sandbox/win/src/handle_closer_test.cc create mode 100644 sandbox/win/src/handle_dispatcher.cc create mode 100644 sandbox/win/src/handle_dispatcher.h create mode 100644 sandbox/win/src/handle_interception.cc create mode 100644 sandbox/win/src/handle_interception.h create mode 100644 sandbox/win/src/handle_policy.cc create mode 100644 sandbox/win/src/handle_policy.h create mode 100644 sandbox/win/src/handle_policy_test.cc create mode 100644 sandbox/win/src/handle_table.cc create mode 100644 sandbox/win/src/handle_table.h create mode 100644 sandbox/win/src/integrity_level_test.cc create mode 100644 sandbox/win/src/interception.cc create mode 100644 sandbox/win/src/interception.h create mode 100644 sandbox/win/src/interception_agent.cc create mode 100644 sandbox/win/src/interception_agent.h create mode 100644 sandbox/win/src/interception_internal.h create mode 100644 sandbox/win/src/interception_unittest.cc create mode 100644 sandbox/win/src/interceptors.h create mode 100644 sandbox/win/src/interceptors_64.cc create mode 100644 sandbox/win/src/interceptors_64.h create mode 100644 sandbox/win/src/internal_types.h create mode 100644 sandbox/win/src/ipc_ping_test.cc create mode 100644 sandbox/win/src/ipc_tags.h create mode 100644 sandbox/win/src/ipc_unittest.cc create mode 100644 sandbox/win/src/job.cc create mode 100644 sandbox/win/src/job.h create mode 100644 sandbox/win/src/job_unittest.cc create mode 100644 sandbox/win/src/named_pipe_dispatcher.cc create mode 100644 sandbox/win/src/named_pipe_dispatcher.h create mode 100644 sandbox/win/src/named_pipe_interception.cc create mode 100644 sandbox/win/src/named_pipe_interception.h create mode 100644 sandbox/win/src/named_pipe_policy.cc create mode 100644 sandbox/win/src/named_pipe_policy.h create mode 100644 sandbox/win/src/named_pipe_policy_test.cc create mode 100644 sandbox/win/src/nt_internals.h create mode 100644 sandbox/win/src/policy_broker.cc create mode 100644 sandbox/win/src/policy_broker.h create mode 100644 sandbox/win/src/policy_engine_opcodes.cc create mode 100644 sandbox/win/src/policy_engine_opcodes.h create mode 100644 sandbox/win/src/policy_engine_params.h create mode 100644 sandbox/win/src/policy_engine_processor.cc create mode 100644 sandbox/win/src/policy_engine_processor.h create mode 100644 sandbox/win/src/policy_engine_unittest.cc create mode 100644 sandbox/win/src/policy_low_level.cc create mode 100644 sandbox/win/src/policy_low_level.h create mode 100644 sandbox/win/src/policy_low_level_unittest.cc create mode 100644 sandbox/win/src/policy_opcodes_unittest.cc create mode 100644 sandbox/win/src/policy_params.h create mode 100644 sandbox/win/src/policy_target.cc create mode 100644 sandbox/win/src/policy_target.h create mode 100644 sandbox/win/src/policy_target_test.cc create mode 100644 sandbox/win/src/process_policy_test.cc create mode 100644 sandbox/win/src/process_thread_dispatcher.cc create mode 100644 sandbox/win/src/process_thread_dispatcher.h create mode 100644 sandbox/win/src/process_thread_interception.cc create mode 100644 sandbox/win/src/process_thread_interception.h create mode 100644 sandbox/win/src/process_thread_policy.cc create mode 100644 sandbox/win/src/process_thread_policy.h create mode 100644 sandbox/win/src/registry_dispatcher.cc create mode 100644 sandbox/win/src/registry_dispatcher.h create mode 100644 sandbox/win/src/registry_interception.cc create mode 100644 sandbox/win/src/registry_interception.h create mode 100644 sandbox/win/src/registry_policy.cc create mode 100644 sandbox/win/src/registry_policy.h create mode 100644 sandbox/win/src/registry_policy_test.cc create mode 100644 sandbox/win/src/resolver.cc create mode 100644 sandbox/win/src/resolver.h create mode 100644 sandbox/win/src/resolver_32.cc create mode 100644 sandbox/win/src/resolver_64.cc create mode 100644 sandbox/win/src/restricted_token.cc create mode 100644 sandbox/win/src/restricted_token.h create mode 100644 sandbox/win/src/restricted_token_unittest.cc create mode 100644 sandbox/win/src/restricted_token_utils.cc create mode 100644 sandbox/win/src/restricted_token_utils.h create mode 100644 sandbox/win/src/sandbox.cc create mode 100644 sandbox/win/src/sandbox.h create mode 100644 sandbox/win/src/sandbox.vcproj create mode 100644 sandbox/win/src/sandbox_factory.h create mode 100644 sandbox/win/src/sandbox_nt_types.h create mode 100644 sandbox/win/src/sandbox_nt_util.cc create mode 100644 sandbox/win/src/sandbox_nt_util.h create mode 100644 sandbox/win/src/sandbox_policy.h create mode 100644 sandbox/win/src/sandbox_policy_base.cc create mode 100644 sandbox/win/src/sandbox_policy_base.h create mode 100644 sandbox/win/src/sandbox_types.h create mode 100644 sandbox/win/src/sandbox_utils.cc create mode 100644 sandbox/win/src/sandbox_utils.h create mode 100644 sandbox/win/src/security_level.h create mode 100644 sandbox/win/src/service_resolver.cc create mode 100644 sandbox/win/src/service_resolver.h create mode 100644 sandbox/win/src/service_resolver_32.cc create mode 100644 sandbox/win/src/service_resolver_64.cc create mode 100644 sandbox/win/src/service_resolver_unittest.cc create mode 100644 sandbox/win/src/shared_handles.cc create mode 100644 sandbox/win/src/shared_handles.h create mode 100644 sandbox/win/src/sharedmem_ipc_client.cc create mode 100644 sandbox/win/src/sharedmem_ipc_client.h create mode 100644 sandbox/win/src/sharedmem_ipc_server.cc create mode 100644 sandbox/win/src/sharedmem_ipc_server.h create mode 100644 sandbox/win/src/sid.cc create mode 100644 sandbox/win/src/sid.h create mode 100644 sandbox/win/src/sid_unittest.cc create mode 100644 sandbox/win/src/sidestep/ia32_modrm_map.cpp create mode 100644 sandbox/win/src/sidestep/ia32_opcode_map.cpp create mode 100644 sandbox/win/src/sidestep/mini_disassembler.cpp create mode 100644 sandbox/win/src/sidestep/mini_disassembler.h create mode 100644 sandbox/win/src/sidestep/mini_disassembler_types.h create mode 100644 sandbox/win/src/sidestep/preamble_patcher.h create mode 100644 sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp create mode 100644 sandbox/win/src/sidestep_resolver.cc create mode 100644 sandbox/win/src/sidestep_resolver.h create mode 100644 sandbox/win/src/sync_dispatcher.cc create mode 100644 sandbox/win/src/sync_dispatcher.h create mode 100644 sandbox/win/src/sync_interception.cc create mode 100644 sandbox/win/src/sync_interception.h create mode 100644 sandbox/win/src/sync_policy.cc create mode 100644 sandbox/win/src/sync_policy.h create mode 100644 sandbox/win/src/sync_policy_test.cc create mode 100644 sandbox/win/src/target_interceptions.cc create mode 100644 sandbox/win/src/target_interceptions.h create mode 100644 sandbox/win/src/target_process.cc create mode 100644 sandbox/win/src/target_process.h create mode 100644 sandbox/win/src/target_services.cc create mode 100644 sandbox/win/src/target_services.h create mode 100644 sandbox/win/src/threadpool_unittest.cc create mode 100644 sandbox/win/src/unload_dll_test.cc create mode 100644 sandbox/win/src/win2k_threadpool.cc create mode 100644 sandbox/win/src/win2k_threadpool.h create mode 100644 sandbox/win/src/win_utils.cc create mode 100644 sandbox/win/src/win_utils.h create mode 100644 sandbox/win/src/win_utils_unittest.cc create mode 100644 sandbox/win/src/window.cc create mode 100644 sandbox/win/src/window.h create mode 100644 sandbox/win/tests/common/controller.cc create mode 100644 sandbox/win/tests/common/controller.h create mode 100644 sandbox/win/tests/common/test_utils.cc create mode 100644 sandbox/win/tests/common/test_utils.h create mode 100644 sandbox/win/tests/integration_tests/integration_tests.cc create mode 100644 sandbox/win/tests/integration_tests/integration_tests_test.cc create mode 100644 sandbox/win/tests/integration_tests/sbox_integration_tests.vcproj create mode 100644 sandbox/win/tests/unit_tests/sbox_unittests.vcproj create mode 100644 sandbox/win/tests/unit_tests/unit_tests.cc create mode 100644 sandbox/win/tests/validation_tests/commands.cc create mode 100644 sandbox/win/tests/validation_tests/commands.h create mode 100644 sandbox/win/tests/validation_tests/sbox_validation_tests.vcproj create mode 100644 sandbox/win/tests/validation_tests/suite.cc create mode 100644 sandbox/win/tests/validation_tests/unit_tests.cc create mode 100644 sandbox/win/tools/finder/finder.cc create mode 100644 sandbox/win/tools/finder/finder.h create mode 100644 sandbox/win/tools/finder/finder.vcproj create mode 100644 sandbox/win/tools/finder/finder_fs.cc create mode 100644 sandbox/win/tools/finder/finder_kernel.cc create mode 100644 sandbox/win/tools/finder/finder_registry.cc create mode 100644 sandbox/win/tools/finder/main.cc create mode 100644 sandbox/win/tools/finder/ntundoc.h create mode 100644 sandbox/win/tools/launcher/launcher.cc create mode 100644 sandbox/win/tools/launcher/launcher.vcproj create mode 100644 sandbox/win/wow_helper.sln create mode 100644 sandbox/win/wow_helper/service64_resolver.cc create mode 100644 sandbox/win/wow_helper/service64_resolver.h create mode 100644 sandbox/win/wow_helper/target_code.cc create mode 100644 sandbox/win/wow_helper/target_code.h create mode 100644 sandbox/win/wow_helper/wow_helper.cc create mode 100755 sandbox/win/wow_helper/wow_helper.exe create mode 100644 sandbox/win/wow_helper/wow_helper.pdb create mode 100644 sandbox/win/wow_helper/wow_helper.vcproj delete mode 100644 sandbox/wow_helper.sln delete mode 100644 sandbox/wow_helper/service64_resolver.cc delete mode 100644 sandbox/wow_helper/service64_resolver.h delete mode 100644 sandbox/wow_helper/target_code.cc delete mode 100644 sandbox/wow_helper/target_code.h delete mode 100644 sandbox/wow_helper/wow_helper.cc delete mode 100755 sandbox/wow_helper/wow_helper.exe delete mode 100644 sandbox/wow_helper/wow_helper.pdb delete mode 100644 sandbox/wow_helper/wow_helper.vcproj (limited to 'sandbox') diff --git a/sandbox/sandbox.gyp b/sandbox/sandbox.gyp index cd42434..1d87d11 100644 --- a/sandbox/sandbox.gyp +++ b/sandbox/sandbox.gyp @@ -9,7 +9,7 @@ 'conditions': [ [ 'OS=="win"', { 'includes': [ - 'sandbox_win.gypi', + 'win/sandbox_win.gypi', ], }], [ 'OS=="linux"', { diff --git a/sandbox/sandbox_poc/main_ui_window.cc b/sandbox/sandbox_poc/main_ui_window.cc deleted file mode 100644 index ef4d550..0000000 --- a/sandbox/sandbox_poc/main_ui_window.cc +++ /dev/null @@ -1,668 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sandbox/sandbox_poc/main_ui_window.h" -#include "base/logging.h" -#include "sandbox/sandbox_poc/resource.h" -#include "sandbox/src/acl.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/win_utils.h" - -HWND MainUIWindow::list_view_ = NULL; - -const wchar_t MainUIWindow::kDefaultDll_[] = L"\\POCDLL.dll"; -const wchar_t MainUIWindow::kDefaultEntryPoint_[] = L"Run"; -const wchar_t MainUIWindow::kDefaultLogFile_[] = L""; - -MainUIWindow::MainUIWindow() - : instance_handle_(NULL), - spawn_target_(L""), - dll_path_(L""), - entry_point_(L""), - broker_(NULL) { -} - -MainUIWindow::~MainUIWindow() { -} - -unsigned int MainUIWindow::CreateMainWindowAndLoop( - HINSTANCE instance, - wchar_t* command_line, - int show_command, - sandbox::BrokerServices* broker) { - DCHECK(instance); - DCHECK(command_line); - DCHECK(broker); - - instance_handle_ = instance; - spawn_target_ = command_line; - broker_ = broker; - - // We'll use spawn_target_ later for creating a child process, but - // CreateProcess doesn't like double quotes, so we remove them along with - // tabs and spaces from the start and end of the string - const wchar_t *trim_removal = L" \r\t\""; - spawn_target_.erase(0, spawn_target_.find_first_not_of(trim_removal)); - spawn_target_.erase(spawn_target_.find_last_not_of(trim_removal) + 1); - - WNDCLASSEX window_class = {0}; - window_class.cbSize = sizeof(WNDCLASSEX); - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.lpfnWndProc = MainUIWindow::WndProc; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = instance; - window_class.hIcon = - ::LoadIcon(instance, MAKEINTRESOURCE(IDI_SANDBOX)); - window_class.hCursor = ::LoadCursor(NULL, IDC_ARROW); - window_class.hbrBackground = GetStockBrush(WHITE_BRUSH); - window_class.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_MAIN_UI); - window_class.lpszClassName = L"sandbox_ui_1"; - window_class.hIconSm = NULL; - - INITCOMMONCONTROLSEX controls = { - sizeof(INITCOMMONCONTROLSEX), - ICC_STANDARD_CLASSES | ICC_LISTVIEW_CLASSES - }; - ::InitCommonControlsEx(&controls); - - if (!::RegisterClassEx(&window_class)) - return ::GetLastError(); - - // Create a main window of size 600x400 - HWND window = ::CreateWindowW(window_class.lpszClassName, - L"", // window name - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, // x - CW_USEDEFAULT, // y - 600, // width - 400, // height - NULL, // parent - NULL, // NULL = use class menu - instance, - 0); // lpParam - - if (NULL == window) - return ::GetLastError(); - - ::SetWindowLongPtr(window, - GWLP_USERDATA, - reinterpret_cast(this)); - - ::SetWindowText(window, L"Sandbox Proof of Concept"); - - ::ShowWindow(window, show_command); - - MSG message; - // Now lets start the message pump retrieving messages for any window that - // belongs to the current thread - while (::GetMessage(&message, NULL, 0, 0)) { - ::TranslateMessage(&message); - ::DispatchMessage(&message); - } - - return 0; -} - -LRESULT CALLBACK MainUIWindow::WndProc(HWND window, - UINT message_id, - WPARAM wparam, - LPARAM lparam) { - MainUIWindow* host = FromWindow(window); - - #define HANDLE_MSG(hwnd, message, fn) \ - case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn)) - - switch (message_id) { - case WM_CREATE: - // 'host' is not yet available when we get the WM_CREATE message - return HANDLE_WM_CREATE(window, wparam, lparam, OnCreate); - case WM_DESTROY: - return HANDLE_WM_DESTROY(window, wparam, lparam, host->OnDestroy); - case WM_SIZE: - return HANDLE_WM_SIZE(window, wparam, lparam, host->OnSize); - case WM_COMMAND: { - // Look at which menu item was clicked on (or which accelerator) - int id = LOWORD(wparam); - switch (id) { - case ID_FILE_EXIT: - host->OnFileExit(); - break; - case ID_COMMANDS_SPAWNTARGET: - host->OnCommandsLaunch(window); - break; - default: - // Some other menu item or accelerator - break; - } - - return ERROR_SUCCESS; - } - - default: - // Some other WM_message, let it pass to DefWndProc - break; - } - - return DefWindowProc(window, message_id, wparam, lparam); -} - -INT_PTR CALLBACK MainUIWindow::SpawnTargetWndProc(HWND dialog, - UINT message_id, - WPARAM wparam, - LPARAM lparam) { - UNREFERENCED_PARAMETER(lparam); - - // Grab a reference to the main UI window (from the window handle) - MainUIWindow* host = FromWindow(GetParent(dialog)); - DCHECK(host); - - switch (message_id) { - case WM_INITDIALOG: { - // Initialize the window text for DLL name edit box - HWND edit_box_dll_name = ::GetDlgItem(dialog, IDC_DLL_NAME); - wchar_t current_dir[MAX_PATH]; - if (GetCurrentDirectory(MAX_PATH, current_dir)) { - std::wstring dll_path = std::wstring(current_dir) + - std::wstring(kDefaultDll_); - ::SetWindowText(edit_box_dll_name, dll_path.c_str()); - } - - // Initialize the window text for Entry Point edit box - HWND edit_box_entry_point = ::GetDlgItem(dialog, IDC_ENTRY_POINT); - ::SetWindowText(edit_box_entry_point, kDefaultEntryPoint_); - - // Initialize the window text for Log File edit box - HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); - ::SetWindowText(edit_box_log_file, kDefaultLogFile_); - - return static_cast(TRUE); - } - case WM_COMMAND: - // If the user presses the OK button (Launch) - if (LOWORD(wparam) == IDOK) { - if (host->OnLaunchDll(dialog)) { - if (host->SpawnTarget()) { - ::EndDialog(dialog, LOWORD(wparam)); - } - } - return static_cast(TRUE); - } else if (LOWORD(wparam) == IDCANCEL) { - // If the user presses the Cancel button - ::EndDialog(dialog, LOWORD(wparam)); - return static_cast(TRUE); - } else if (LOWORD(wparam) == IDC_BROWSE_DLL) { - // If the user presses the Browse button to look for a DLL - std::wstring dll_path = host->OnShowBrowseForDllDlg(dialog); - if (dll_path.length() > 0) { - // Initialize the window text for Log File edit box - HWND edit_box_dll_path = ::GetDlgItem(dialog, IDC_DLL_NAME); - ::SetWindowText(edit_box_dll_path, dll_path.c_str()); - } - return static_cast(TRUE); - } else if (LOWORD(wparam) == IDC_BROWSE_LOG) { - // If the user presses the Browse button to look for a log file - std::wstring log_path = host->OnShowBrowseForLogFileDlg(dialog); - if (log_path.length() > 0) { - // Initialize the window text for Log File edit box - HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); - ::SetWindowText(edit_box_log_file, log_path.c_str()); - } - return static_cast(TRUE); - } - - break; - } - - return static_cast(FALSE); -} - -MainUIWindow* MainUIWindow::FromWindow(HWND main_window) { - // We store a 'this' pointer using SetWindowLong in CreateMainWindowAndLoop - // so that we can retrieve it with this function later. This prevents us - // from having to define all the message handling functions (that we refer to - // in the window proc) as static - ::GetWindowLongPtr(main_window, GWLP_USERDATA); - return reinterpret_cast( - ::GetWindowLongPtr(main_window, GWLP_USERDATA)); -} - -BOOL MainUIWindow::OnCreate(HWND parent_window, LPCREATESTRUCT) { - // Create the listview that will the main app UI - list_view_ = ::CreateWindow(WC_LISTVIEW, // Class name - L"", // Window name - WS_CHILD | WS_VISIBLE | LVS_REPORT | - LVS_NOCOLUMNHEADER | WS_BORDER, - 0, // x - 0, // y - 0, // width - 0, // height - parent_window, // parent - NULL, // menu - ::GetModuleHandle(NULL), - 0); // lpParam - - DCHECK(list_view_); - if (!list_view_) - return FALSE; - - LVCOLUMN list_view_column = {0}; - list_view_column.mask = LVCF_FMT | LVCF_WIDTH ; - list_view_column.fmt = LVCFMT_LEFT; - list_view_column.cx = 10000; // Maximum size of an entry in the list view. - ListView_InsertColumn(list_view_, 0, &list_view_column); - - // Set list view to show green font on black background - ListView_SetBkColor(list_view_, CLR_NONE); - ListView_SetTextColor(list_view_, RGB(0x0, 0x0, 0x0)); - ListView_SetTextBkColor(list_view_, CLR_NONE); - - return TRUE; -} - -void MainUIWindow::OnDestroy(HWND window) { - UNREFERENCED_PARAMETER(window); - - // Post a quit message because our application is over when the - // user closes this window. - ::PostQuitMessage(0); -} - -void MainUIWindow::OnSize(HWND window, UINT state, int cx, int cy) { - UNREFERENCED_PARAMETER(window); - UNREFERENCED_PARAMETER(state); - - // If we have a valid inner child, resize it to cover the entire - // client area of the main UI window. - if (list_view_) { - ::MoveWindow(list_view_, - 0, // x - 0, // y - cx, // width - cy, // height - TRUE); // repaint - } -} - -void MainUIWindow::OnPaint(HWND window) { - PAINTSTRUCT paintstruct; - ::BeginPaint(window, &paintstruct); - // add painting code here if required - ::EndPaint(window, &paintstruct); -} - -void MainUIWindow::OnFileExit() { - ::PostQuitMessage(0); -} - -void MainUIWindow::OnCommandsLaunch(HWND window) { - // User wants to see the Select DLL dialog box - ::DialogBox(instance_handle_, - MAKEINTRESOURCE(IDD_LAUNCH_DLL), - window, - SpawnTargetWndProc); -} - -bool MainUIWindow::OnLaunchDll(HWND dialog) { - HWND edit_box_dll_name = ::GetDlgItem(dialog, IDC_DLL_NAME); - HWND edit_box_entry_point = ::GetDlgItem(dialog, IDC_ENTRY_POINT); - HWND edit_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); - - wchar_t dll_path[MAX_PATH]; - wchar_t entry_point[MAX_PATH]; - wchar_t log_file[MAX_PATH]; - - int dll_name_len = ::GetWindowText(edit_box_dll_name, dll_path, MAX_PATH); - int entry_point_len = ::GetWindowText(edit_box_entry_point, - entry_point, MAX_PATH); - // Log file is optional (can be blank) - ::GetWindowText(edit_log_file, log_file, MAX_PATH); - - if (0 >= dll_name_len) { - ::MessageBox(dialog, - L"Please specify a DLL for the target to load", - L"No DLL specified", - MB_ICONERROR); - return false; - } - - if (GetFileAttributes(dll_path) == INVALID_FILE_ATTRIBUTES) { - ::MessageBox(dialog, - L"DLL specified was not found", - L"DLL not found", - MB_ICONERROR); - return false; - } - - if (0 >= entry_point_len) { - ::MessageBox(dialog, - L"Please specify an entry point for the DLL", - L"No entry point specified", - MB_ICONERROR); - return false; - } - - // store these values in the member variables for use in SpawnTarget - log_file_ = std::wstring(L"\"") + log_file + std::wstring(L"\""); - dll_path_ = dll_path; - entry_point_ = entry_point; - - return true; -} - -DWORD WINAPI MainUIWindow::ListenPipeThunk(void *param) { - return reinterpret_cast(param)->ListenPipe(); -} - -DWORD WINAPI MainUIWindow::WaitForTargetThunk(void *param) { - return reinterpret_cast(param)->WaitForTarget(); -} - -// Thread waiting for the target application to die. It displays -// a message in the list view when it happens. -DWORD MainUIWindow::WaitForTarget() { - WaitForSingleObject(target_.hProcess, INFINITE); - - DWORD exit_code = 0; - if (!GetExitCodeProcess(target_.hProcess, &exit_code)) { - exit_code = 0xFFFF; // Default exit code - } - - ::CloseHandle(target_.hProcess); - ::CloseHandle(target_.hThread); - - AddDebugMessage(L"Targed exited with return code %d", exit_code); - return 0; -} - -// Thread waiting for messages on the log pipe. It displays the messages -// in the listview. -DWORD MainUIWindow::ListenPipe() { - HANDLE logfile_handle = NULL; - ATL::CString file_to_open = log_file_.c_str(); - file_to_open.Remove(L'\"'); - if (file_to_open.GetLength()) { - logfile_handle = ::CreateFile(file_to_open.GetBuffer(), - GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, // Default security attributes - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); // No template - if (INVALID_HANDLE_VALUE == logfile_handle) { - AddDebugMessage(L"Failed to open \"%ls\" for logging. Error %d", - file_to_open.GetBuffer(), ::GetLastError()); - logfile_handle = NULL; - } - } - - const int kSizeBuffer = 1024; - BYTE read_buffer[kSizeBuffer] = {0}; - ATL::CStringA read_buffer_global; - ATL::CStringA string_to_print; - - DWORD last_error = 0; - while(last_error == ERROR_SUCCESS || last_error == ERROR_PIPE_LISTENING || - last_error == ERROR_NO_DATA) - { - DWORD read_data_length; - if (::ReadFile(pipe_handle_, - read_buffer, - kSizeBuffer - 1, // Max read size - &read_data_length, - NULL)) { // Not overlapped - if (logfile_handle) { - DWORD write_data_length; - ::WriteFile(logfile_handle, - read_buffer, - read_data_length, - &write_data_length, - FALSE); // Not overlapped - } - - // Append the new buffer to the current buffer - read_buffer[read_data_length] = NULL; - read_buffer_global += reinterpret_cast(read_buffer); - read_buffer_global.Remove(10); // Remove the CRs - - // If we completed a new line, output it - int endline = read_buffer_global.Find(13); // search for LF - while (-1 != endline) { - string_to_print = read_buffer_global; - string_to_print.Delete(endline, string_to_print.GetLength()); - read_buffer_global.Delete(0, endline); - - // print the line (with the ending LF) - OutputDebugStringA(string_to_print.GetBuffer()); - - // Remove the ending LF - read_buffer_global.Delete(0, 1); - - // Add the line to the log - AddDebugMessage(L"%S", string_to_print.GetBuffer()); - - endline = read_buffer_global.Find(13); - } - last_error = ERROR_SUCCESS; - } else { - last_error = GetLastError(); - Sleep(100); - } - } - - if (read_buffer_global.GetLength()) { - AddDebugMessage(L"%S", read_buffer_global.GetBuffer()); - } - - CloseHandle(pipe_handle_); - - if (logfile_handle) { - CloseHandle(logfile_handle); - } - - return 0; -} - -bool MainUIWindow::SpawnTarget() { - // Generate the pipe name - GUID random_id; - CoCreateGuid(&random_id); - - wchar_t log_pipe[MAX_PATH] = {0}; - wnsprintf(log_pipe, MAX_PATH - 1, - L"\\\\.\\pipe\\sbox_pipe_log_%lu_%lu_%lu_%lu", - random_id.Data1, - random_id.Data2, - random_id.Data3, - random_id.Data4); - - // We concatenate the four strings, add three spaces and a zero termination - // We use the resulting string as a param to CreateProcess (in SpawnTarget) - // Documented maximum for command line in CreateProcess is 32K (msdn) - size_t size_call = spawn_target_.length() + entry_point_.length() + - dll_path_.length() + wcslen(log_pipe) + 6; - if (32 * 1024 < (size_call * sizeof(wchar_t))) { - AddDebugMessage(L"The length of the arguments exceeded 32K. " - L"Aborting operation."); - return false; - } - - wchar_t * arguments = new wchar_t[size_call]; - wnsprintf(arguments, static_cast(size_call), L"%ls %ls \"%ls\" %ls", - spawn_target_.c_str(), entry_point_.c_str(), - dll_path_.c_str(), log_pipe); - - arguments[size_call - 1] = L'\0'; - - sandbox::TargetPolicy* policy = broker_->CreatePolicy(); - policy->SetJobLevel(sandbox::JOB_LOCKDOWN, 0); - policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, - sandbox::USER_LOCKDOWN); - policy->SetAlternateDesktop(true); - policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); - - // Set the rule to allow the POC dll to be loaded by the target. Note that - // the rule allows 'all access' to the DLL, which could mean that the target - // could modify the DLL on disk. - policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, - sandbox::TargetPolicy::FILES_ALLOW_ANY, dll_path_.c_str()); - - sandbox::ResultCode result = broker_->SpawnTarget(spawn_target_.c_str(), - arguments, policy, - &target_); - - policy->Release(); - policy = NULL; - - bool return_value = false; - if (sandbox::SBOX_ALL_OK != result) { - AddDebugMessage( - L"Failed to spawn target %ls w/args (%ls), sandbox error code: %d", - spawn_target_.c_str(), arguments, result); - return_value = false; - } else { - - DWORD thread_id; - ::CreateThread(NULL, // Default security attributes - NULL, // Default stack size - &MainUIWindow::WaitForTargetThunk, - this, - 0, // No flags - &thread_id); - - pipe_handle_ = ::CreateNamedPipe(log_pipe, - PIPE_ACCESS_INBOUND | WRITE_DAC, - PIPE_TYPE_MESSAGE | PIPE_NOWAIT, - 1, // Number of instances. - 512, // Out buffer size. - 512, // In buffer size. - NMPWAIT_USE_DEFAULT_WAIT, - NULL); // Default security descriptor - - if (INVALID_HANDLE_VALUE == pipe_handle_) - AddDebugMessage(L"Failed to create pipe. Error %d", ::GetLastError()); - - if (!sandbox::AddKnownSidToKernelObject(pipe_handle_, WinWorldSid, - FILE_ALL_ACCESS)) - AddDebugMessage(L"Failed to set security on pipe. Error %d", - ::GetLastError()); - - ::CreateThread(NULL, // Default security attributes - NULL, // Default stack size - &MainUIWindow::ListenPipeThunk, - this, - 0, // No flags - &thread_id); - - ::ResumeThread(target_.hThread); - - AddDebugMessage(L"Successfully spawned target w/args (%ls)", arguments); - return_value = true; - } - - delete[] arguments; - return return_value; -} - -std::wstring MainUIWindow::OnShowBrowseForDllDlg(HWND owner) { - wchar_t filename[MAX_PATH]; - wcscpy_s(filename, MAX_PATH, L""); - - OPENFILENAMEW file_info = {0}; - file_info.lStructSize = sizeof(file_info); - file_info.hwndOwner = owner; - file_info.lpstrFile = filename; - file_info.nMaxFile = MAX_PATH; - file_info.lpstrFilter = L"DLL files (*.dll)\0*.dll\0All files\0*.*\0\0\0"; - - file_info.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; - - if (GetOpenFileName(&file_info)) { - return file_info.lpstrFile; - } - - return L""; -} - -std::wstring MainUIWindow::OnShowBrowseForLogFileDlg(HWND owner) { - wchar_t filename[MAX_PATH]; - wcscpy_s(filename, MAX_PATH, L""); - - OPENFILENAMEW file_info = {0}; - file_info.lStructSize = sizeof(file_info); - file_info.hwndOwner = owner; - file_info.lpstrFile = filename; - file_info.nMaxFile = MAX_PATH; - file_info.lpstrFilter = L"Log file (*.txt)\0*.txt\0All files\0*.*\0\0\0"; - - file_info.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; - - if (GetSaveFileName(&file_info)) { - return file_info.lpstrFile; - } - - return L""; -} - -void MainUIWindow::AddDebugMessage(const wchar_t* format, ...) { - DCHECK(format); - if (!format) - return; - - const int kMaxDebugBuffSize = 1024; - - va_list arg_list; - _crt_va_start(arg_list, format); - - wchar_t text[kMaxDebugBuffSize + 1]; - vswprintf_s(text, kMaxDebugBuffSize, format, arg_list); - text[kMaxDebugBuffSize] = L'\0'; - - InsertLineInListView(text); -} - - -void MainUIWindow::InsertLineInListView(wchar_t* debug_message) { - DCHECK(debug_message); - if (!debug_message) - return; - - // Prepend the time to the message - const int kSizeTime = 100; - size_t size_message_with_time = wcslen(debug_message) + kSizeTime; - wchar_t * message_time = new wchar_t[size_message_with_time]; - - time_t time_temp; - time_temp = time(NULL); - - struct tm time = {0}; - localtime_s(&time, &time_temp); - - size_t return_code; - return_code = wcsftime(message_time, kSizeTime, L"[%H:%M:%S] ", &time); - - wcscat_s(message_time, size_message_with_time, debug_message); - - // We add the debug message to the top of the listview - LVITEM item; - item.iItem = ListView_GetItemCount(list_view_); - item.iSubItem = 0; - item.mask = LVIF_TEXT | LVIF_PARAM; - item.pszText = message_time; - item.lParam = 0; - - ListView_InsertItem(list_view_, &item); - - delete[] message_time; -} diff --git a/sandbox/sandbox_poc/main_ui_window.h b/sandbox/sandbox_poc/main_ui_window.h deleted file mode 100644 index c70189a..0000000 --- a/sandbox/sandbox_poc/main_ui_window.h +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__ -#define SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__ - -#include - -#include "base/basictypes.h" - -namespace sandbox { -class BrokerServices; -enum ResultCode; -} - -// Header file for the MainUIWindow, a simple window with a menu bar that -// can pop up a dialog (accessible through the menu bar), to specify a path and -// filename of any DLL to load and choose an entry point of his/her choice -// (note: only entry points with no parameters are expected to work). -// -// The purpose of this is to be able to spawn an EXE inside a SandBox, have it -// load a DLL and call the entry point on it to test how it behaves inside the -// sandbox. This is useful for developer debugging and for security testing. -// -// The MainUIWindow also has a listview that displays debugging information to -// the user. -// -// Sample usage: -// -// MainUIWindow window; -// unsigned int ret = window.CreateMainWindowAndLoop( -// handle_to_current_instance, -// ::GetCommandLineW(), -// show_command, -// broker); -// -// The CreateMainWindowAndLoop() contains a message loop that ends when the -// user closes the MainUIWindow. - -// This class encapsulates the Main UI window for the broker application. -// It simply shows a menu that gives the user the ability (through a dialog) to -// specify a DLL and what entry point to call in the DLL. -class MainUIWindow { - public: - MainUIWindow(); - ~MainUIWindow(); - - // Creates the main window, displays it and starts the message pump. This - // call will not return until user closes the main UI window that appears - // as a result. Arguments 'instance', 'command_line' and 'show_cmd' can be - // passed in directly from winmain. The 'broker' argument is a pointer to a - // BrokerService that will launch a new EXE inside the sandbox and load the - // DLL of the user's choice. - unsigned int CreateMainWindowAndLoop(HINSTANCE instance, - wchar_t* command_line, - int show_command, - sandbox::BrokerServices* broker); - - private: - // The default value DLL name to add to the edit box. - static const wchar_t kDefaultDll_[]; - - // The default value to show in the entry point. - static const wchar_t kDefaultEntryPoint_[]; - - // The default value to show in the log file. - static const wchar_t kDefaultLogFile_[]; - - // Handles the messages sent to the main UI window. The return value is the - // result of the message processing and depends on the message. - static LRESULT CALLBACK WndProc(HWND window, - UINT message_id, - WPARAM wparam, - LPARAM lparam); - - // Handles the messages sent to the SpawnTarget dialog. The return value is - // the result of the message processing and depends on the message. - static INT_PTR CALLBACK SpawnTargetWndProc(HWND dialog, - UINT message_id, - WPARAM wparam, - LPARAM lparam); - - // Retrieves a pointer to the MainWindow from a value stored along with the - // window handle (passed in as hwnd). Return value is a pointer to the - // MainUIWindow previously stored with SetWindowLong() during WM_CREATE. - static MainUIWindow* FromWindow(HWND main_window); - - // Handles the WM_CREATE message for the main UI window. Returns TRUE on - // success. - static BOOL OnCreate(HWND parent_window, LPCREATESTRUCT); - - // Handles the WM_DESTROY message for the main UI window. - void OnDestroy(HWND window); - - // Handles the WM_SIZE message for the main UI window. - void OnSize(HWND window, UINT state, int cx, int cy); - - // Handles the WM_PAINT message for the main UI window. - void OnPaint(HWND window); - - // Handles the menu command File \ Exit for the main UI window. - void OnFileExit(); - - // Handles the menu command Commands \ Launch for the main UI window. - void OnCommandsLaunch(HWND window); - - // Handles the Launch button in the SpawnTarget dialog (normally clicked - // after selecting DLL and entry point). OnLaunchDll will retrieve the - // values entered by the user and store it in the members of the class. - // Returns true if user selected a non-zero values for DLL filename - // (possibly including path also) and entry point. - bool OnLaunchDll(HWND dialog); - - // Spawns a target EXE inside the sandbox (with the help of the - // BrokerServices passed in to CreateMainWindowAndLoop), and passes to it - // (as command line arguments) the DLL path and the entry point function - // name. The EXE is expected to parse the command line and load the DLL. - // NOTE: The broker does not know if the target EXE successfully loaded the - // DLL, for that you have to rely on the EXE providing a log. - // Returns true if the broker reports that it was successful in creating - // the target and false if not. - bool SpawnTarget(); - - // Shows a standard File Open dialog and returns the DLL filename selected or - // blank string if the user cancelled (or an error occurred). - std::wstring OnShowBrowseForDllDlg(HWND owner); - - // Shows a standard Save As dialog and returns the log filename selected or - // blank string if the user cancelled (or an error occurred). - std::wstring OnShowBrowseForLogFileDlg(HWND owner); - - // Formats a message using the supplied format string and prints it in the - // listview in the main UI window. Passing a NULL param in 'fmt' results in - // no action being performed. Maximum message length is 1K. - void AddDebugMessage(const wchar_t* format, ...); - - // Assists AddDebugMessage in displaying a message in the ListView. It - // simply wraps ListView_InsertItem to insert a debugging message to the - // top of the list view. Passing a NULL param in 'fmt' results in no action - // being performed. - void InsertLineInListView(wchar_t* debug_message); - - // Calls ListenPipe using the class instance received in parameter. This is - // used to create new threads executing ListenPipe - static DWORD WINAPI ListenPipeThunk(void *param); - - // Calls WaitForTargetThunk using the class instance received in parameter - // This is used to create new threads executing WaitForTarget. - static DWORD WINAPI WaitForTargetThunk(void *param); - - // Listens on a pipe and output the data received to a file and to the UI. - DWORD ListenPipe(); - - // Waits for the target to dies and display a message in the UI. - DWORD WaitForTarget(); - - // The BrokerServices will be used to spawn an EXE in a sandbox and ask - // it to load a DLL. - sandbox::BrokerServices* broker_; - - // Contains the information about the running target. - PROCESS_INFORMATION target_; - - // This is essentially a command line to a target executable that the - // broker will spawn and ask to load the DLL. - std::wstring spawn_target_; - - // A handle to the current instance of the app. Passed in to this class - // through CreateMainWindowAndLoop. - HINSTANCE instance_handle_; - - // A path to the DLL that the target should load once it executes. - std::wstring dll_path_; - - // The name of the entry point the target should call after it loads the DLL. - std::wstring entry_point_; - - // The name of the log file to use. - std::wstring log_file_; - - // This is a static handle to the list view that fills up the entire main - // UI window. The list view is used to display debugging information to the - // user. - static HWND list_view_; - - // Pipe used to communicate the logs between the target and the broker. - HANDLE pipe_handle_; - - DISALLOW_COPY_AND_ASSIGN(MainUIWindow); -}; - -#endif // SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__ diff --git a/sandbox/sandbox_poc/pocdll/exports.h b/sandbox/sandbox_poc/pocdll/exports.h deleted file mode 100644 index 66a07d6..0000000 --- a/sandbox/sandbox_poc/pocdll/exports.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SANDBOX_POC_POCDLL_EXPORTS_H__ -#define SANDBOX_SANDBOX_POC_POCDLL_EXPORTS_H__ - -#include - -#ifdef POCDLL_EXPORTS -#define POCDLL_API __declspec(dllexport) __cdecl -#else -#define POCDLL_API __declspec(dllimport) __cdecl -#endif - -extern "C" { -// Tries to open several known system path and outputs -// the result. -// "log" is the handle of the log file. -void POCDLL_API TestFileSystem(HANDLE log); - -// Tries to find all handles open in the process and prints the name of the -// resource references by the handle along with the access right. -// "log" is the handle of the log file. -void POCDLL_API TestGetHandle(HANDLE log); - -// Creates a lot of threads until it cannot create more. The goal of this -// function is to determine if it's possible to crash the machine when we -// flood the machine with new threads -// "log" is the handle of the log file. -void POCDLL_API TestThreadBombing(HANDLE log); - -// Takes all cpu of the machine. For each processor on the machine we assign -// a thread. This thread will compute a mathematical expression over and over -// to take all cpu. -// "log" is the handle of the log file. -// Note: here we are using the affinity to find out how many processors are on -// the machine and to force a thread to run only on a given processor. -void POCDLL_API TestTakeAllCpu(HANDLE log); - -// Creates memory in the heap until it fails 5 times in a row and prints the -// amount of memory created. This function is used to find out if it's possible -// to take all memory on the machine and crash the system. -// "log" is the handle of the log file. -void POCDLL_API TestUseAllMemory(HANDLE log); - -// Creates millions of kernel objects. This function is used to find out if it's -// possible to crash the system if we create too many kernel objects and if we -// hold too many handles. All those kernel objects are unnamed. -// "log" is the handle of the log file. -void POCDLL_API TestCreateObjects(HANDLE log); - -// Receives a hwnd and tries to close it. This is the callback for EnumWindows. -// It will be called for each window(hwnd) on the system. -// "log" is the handle of the log file. -// Always returns TRUE to tell the system that we want to continue the -// enumeration. -void POCDLL_API TestCloseHWND(HANDLE log); - -// Tries to listen on the port 88. -// "log" is the handle of the log file. -void POCDLL_API TestNetworkListen(HANDLE log); - -// Lists all processes on the system and tries to open them -// "log" is the handle of the log file. -void POCDLL_API TestProcesses(HANDLE log); - -// Lists all threads on the system and tries to open them -// "log" is the handle of the log file. -void POCDLL_API TestThreads(HANDLE log); - -// Tries to open some known system registry key and outputs the result. -// "log" is the handle of the log file. -void POCDLL_API TestRegistry(HANDLE log); - -// Records all keystrokes typed for 15 seconds and then display them. -// "log" is the handle of the log file. -void POCDLL_API TestSpyKeys(HANDLE log); - -// Tries to read pixels on the monitor and output if the operation -// failes or succeeded. -// "log" is the handle of the log file. -void POCDLL_API TestSpyScreen(HANDLE log); - -// Runs all tests except those who are invasive -void POCDLL_API Run(HANDLE log); -} - -#endif // SANDBOX_SANDBOX_POC_POCDLL_EXPORTS_H__ diff --git a/sandbox/sandbox_poc/pocdll/fs.cc b/sandbox/sandbox_poc/pocdll/fs.cc deleted file mode 100644 index 42c1006..0000000 --- a/sandbox/sandbox_poc/pocdll/fs.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/sandbox_poc/pocdll/exports.h" -#include "sandbox/sandbox_poc/pocdll/utils.h" - -// This file contains the tests used to verify the security of the file system. - -// Tries to open a file and outputs the result. -// "path" can contain environment variables. -// "output" is the stream for the logging. -void TryOpenFile(wchar_t *path, FILE *output) { - wchar_t path_expanded[MAX_PATH] = {0}; - DWORD size = ::ExpandEnvironmentStrings(path, path_expanded, MAX_PATH - 1); - if (!size) { - fprintf(output, "[ERROR] Cannot expand \"%S\". Error %S.\r\n", path, - ::GetLastError()); - } - - HANDLE file; - file = ::CreateFile(path_expanded, - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, // No security attributes - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - NULL); // No template - - if (file && INVALID_HANDLE_VALUE != file) { - fprintf(output, "[GRANTED] Opening file \"%S\". Handle 0x%p\r\n", path, - file); - ::CloseHandle(file); - } else { - fprintf(output, "[BLOCKED] Opening file \"%S\". Error %d.\r\n", path, - ::GetLastError()); - } -} - -void POCDLL_API TestFileSystem(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - TryOpenFile(L"%SystemDrive%", output); - TryOpenFile(L"%SystemRoot%", output); - TryOpenFile(L"%ProgramFiles%", output); - TryOpenFile(L"%SystemRoot%\\System32", output); - TryOpenFile(L"%SystemRoot%\\explorer.exe", output); - TryOpenFile(L"%SystemRoot%\\Cursors\\arrow_i.cur", output); - TryOpenFile(L"%AllUsersProfile%", output); - TryOpenFile(L"%UserProfile%", output); - TryOpenFile(L"%Temp%", output); - TryOpenFile(L"%AppData%", output); -} diff --git a/sandbox/sandbox_poc/pocdll/handles.cc b/sandbox/sandbox_poc/pocdll/handles.cc deleted file mode 100644 index 05a57b7..0000000 --- a/sandbox/sandbox_poc/pocdll/handles.cc +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/sandbox_poc/pocdll/exports.h" -#include "sandbox/sandbox_poc/pocdll/utils.h" -#include "sandbox/tools/finder/ntundoc.h" - -// This file contains the tests used to verify the security of handles in -// the process - -NTQUERYOBJECT NtQueryObject; -NTQUERYINFORMATIONFILE NtQueryInformationFile; -NTQUERYSYSTEMINFORMATION NtQuerySystemInformation; - -void POCDLL_API TestGetHandle(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - // Initialize the NTAPI functions we need - HMODULE ntdll_handle = ::GetModuleHandle(L"ntdll.dll"); - if (!ntdll_handle) { - fprintf(output, "[ERROR] Cannot load ntdll.dll. Error %d\r\n", - ::GetLastError()); - return; - } - - NtQueryObject = reinterpret_cast( - GetProcAddress(ntdll_handle, "NtQueryObject")); - NtQueryInformationFile = reinterpret_cast( - GetProcAddress(ntdll_handle, "NtQueryInformationFile")); - NtQuerySystemInformation = reinterpret_cast( - GetProcAddress(ntdll_handle, "NtQuerySystemInformation")); - - if (!NtQueryObject || !NtQueryInformationFile || !NtQuerySystemInformation) { - fprintf(output, "[ERROR] Cannot load all NT functions. Error %d\r\n", - ::GetLastError()); - return; - } - - // Get the number of handles on the system - DWORD buffer_size = 0; - SYSTEM_HANDLE_INFORMATION_EX temp_info; - NTSTATUS status = NtQuerySystemInformation( - SystemHandleInformation, &temp_info, sizeof(temp_info), - &buffer_size); - if (!buffer_size) { - fprintf(output, "[ERROR] Get the number of handles. Error 0x%X\r\n", - status); - return; - } - - SYSTEM_HANDLE_INFORMATION_EX *system_handles = - reinterpret_cast(new BYTE[buffer_size]); - - status = NtQuerySystemInformation(SystemHandleInformation, system_handles, - buffer_size, &buffer_size); - if (STATUS_SUCCESS != status) { - fprintf(output, "[ERROR] Failed to get the handle list. Error 0x%X\r\n", - status); - delete [] system_handles; - return; - } - - for (ULONG i = 0; i < system_handles->NumberOfHandles; ++i) { - USHORT h = system_handles->Information[i].Handle; - if (system_handles->Information[i].ProcessId != ::GetCurrentProcessId()) - continue; - - OBJECT_NAME_INFORMATION *name = NULL; - ULONG name_size = 0; - // Query the name information a first time to get the size of the name. - status = NtQueryObject(reinterpret_cast(h), - ObjectNameInformation, - name, - name_size, - &name_size); - - if (name_size) { - name = reinterpret_cast(new BYTE[name_size]); - - // Query the name information a second time to get the name of the - // object referenced by the handle. - status = NtQueryObject(reinterpret_cast(h), - ObjectNameInformation, - name, - name_size, - &name_size); - } - - PUBLIC_OBJECT_TYPE_INFORMATION *type = NULL; - ULONG type_size = 0; - - // Query the object to get the size of the object type name. - status = NtQueryObject(reinterpret_cast(h), - ObjectTypeInformation, - type, - type_size, - &type_size); - if (type_size) { - type = reinterpret_cast( - new BYTE[type_size]); - - // Query the type information a second time to get the object type - // name. - status = NtQueryObject(reinterpret_cast(h), - ObjectTypeInformation, - type, - type_size, - &type_size); - } - - // NtQueryObject cannot return the name for a file. In this case we - // need to ask NtQueryInformationFile - FILE_NAME_INFORMATION *file_name = NULL; - if (type && wcsncmp(L"File", type->TypeName.Buffer, - (type->TypeName.Length / - sizeof(type->TypeName.Buffer[0]))) == 0) { - // This function does not return the size of the buffer. We need to - // iterate and always increase the buffer size until the function - // succeeds. (Or at least does not fail with STATUS_BUFFER_OVERFLOW) - ULONG size_file = MAX_PATH; - IO_STATUS_BLOCK status_block = {0}; - do { - // Delete the previous buffer create. The buffer was too small - if (file_name) { - delete[] reinterpret_cast(file_name); - file_name = NULL; - } - - // Increase the buffer and do the call agan - size_file += MAX_PATH; - file_name = reinterpret_cast( - new BYTE[size_file]); - status = NtQueryInformationFile(reinterpret_cast(h), - &status_block, - file_name, - size_file, - FileNameInformation); - } while (status == STATUS_BUFFER_OVERFLOW); - - if (STATUS_SUCCESS != status) { - if (file_name) { - delete[] file_name; - file_name = NULL; - } - } - } - - if (file_name) { - UNICODE_STRING file_name_string; - file_name_string.Buffer = file_name->FileName; - file_name_string.Length = (USHORT)file_name->FileNameLength; - file_name_string.MaximumLength = (USHORT)file_name->FileNameLength; - fprintf(output, "[GRANTED] Handle 0x%4.4X Access: 0x%8.8X " - "Type: %-13.13wZ Path: %wZ\r\n", - h, - system_handles->Information[i].GrantedAccess, - type ? &type->TypeName : NULL, - &file_name_string); - } else { - fprintf(output, "[GRANTED] Handle 0x%4.4X Access: 0x%8.8X " - "Type: %-13.13wZ Path: %wZ\r\n", - h, - system_handles->Information[i].GrantedAccess, - type ? &type->TypeName : NULL, - name ? &name->ObjectName : NULL); - } - - if (type) { - delete[] type; - } - - if (file_name) { - delete[] file_name; - } - - if (name) { - delete [] name; - } - } - - if (system_handles) { - delete [] system_handles; - } -} diff --git a/sandbox/sandbox_poc/pocdll/invasive.cc b/sandbox/sandbox_poc/pocdll/invasive.cc deleted file mode 100644 index 1bac7c1..0000000 --- a/sandbox/sandbox_poc/pocdll/invasive.cc +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include "sandbox/sandbox_poc/pocdll/exports.h" -#include "sandbox/sandbox_poc/pocdll/utils.h" - -// This file contains the tests used to verify if it's possible to DOS or crash -// the machine. All tests that can impact the stability of the machine should -// be in this file. - -// Sleeps forever. this function is used to be the -// entry point for the threads created by the thread bombing function. -// This function never returns. -DWORD WINAPI MyThreadBombimgFunction(void *param) { - UNREFERENCED_PARAMETER(param); - Sleep(INFINITE); - return 0; -} - -void POCDLL_API TestThreadBombing(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - // we stop after 5 errors in a row - int number_errors = 0; - for (int i = 0; i < 100000; ++i) { - DWORD tid; - // Create the thread and leak the handle. - HANDLE thread = ::CreateThread(NULL, // Default security attributes - NULL, // Stack size - MyThreadBombimgFunction, - NULL, // Parameter - 0, // No creation flags - &tid); - if (thread) { - fprintf(output, "[GRANTED] Creating thread with tid 0x%X\r\n", tid); - ::CloseHandle(thread); - number_errors = 0; - } else { - fprintf(output, "[BLOCKED] Creating thread. Error %d\r\n", - ::GetLastError()); - number_errors++; - } - - if (number_errors >= 5) { - break; - } - } -} - - -// Executes a complex mathematical operation forever in a loop. This function -// is used as entry point for the threads created by TestTakeAllCpu. It it -// designed to take all CPU on the processor where the thread is running. -// The return value is always 0. -DWORD WINAPI TakeAllCpu(void *param) { - UNREFERENCED_PARAMETER(param); - int cpt = 0; - for (;;) { - cpt += 2; - cpt /= 2; - cpt *= cpt; - cpt = cpt % 100; - cpt = cpt | (cpt * cpt); - } -} - -void POCDLL_API TestTakeAllCpu(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - DWORD_PTR process_mask = 0; - DWORD_PTR system_mask = 0; - if (::GetProcessAffinityMask(::GetCurrentProcess(), - &process_mask, - &system_mask)) { - DWORD_PTR affinity_mask = 1; - - while (system_mask) { - DWORD tid = 0; - - HANDLE thread = ::CreateThread(NULL, // Default security attributes. - NULL, // Stack size. - TakeAllCpu, - NULL, // Parameter. - 0, // No creation flags. - &tid); - ::SetThreadAffinityMask(thread, affinity_mask); - - if (::SetThreadPriority(thread, REALTIME_PRIORITY_CLASS)) { - fprintf(output, "[GRANTED] Set thread(%d) priority to Realtime\r\n", - tid); - } else { - fprintf(output, "[BLOCKED] Set thread(%d) priority to Realtime\r\n", - tid); - } - - ::CloseHandle(thread); - - affinity_mask = affinity_mask << 1; - system_mask = system_mask >> 1; - } - } else { - fprintf(output, "[ERROR] Cannot get affinity mask. Error %d\r\n", - ::GetLastError()); - } -} - -void POCDLL_API TestUseAllMemory(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - int number_errors = 0; - unsigned long memory_size = 0; - for (;;) { - DWORD *ptr_to_leak = reinterpret_cast(malloc(1024*256)); - if (ptr_to_leak) { - memory_size += (256); - number_errors = 0; - } else { - number_errors++; - } - - // check if we have more than 5 errors in a row. If so, quit. - if (number_errors >= 5) { - fprintf(output, "[INFO] Created %lu kb of memory\r\n", memory_size); - return; - } - - Sleep(5); // 5ms to be able to see the progression easily with taskmgr. - } -} - -void POCDLL_API TestCreateObjects(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - int mutexes = 0; - int jobs = 0; - int events = 0; - for (int i = 0; i < 1000000; ++i) { - if (::CreateMutex(NULL, // Default security attributes. - TRUE, // We are the initial owner. - NULL)) { // No name. - mutexes++; - } - - if (::CreateJobObject(NULL, // Default security attributes. - NULL)) { // No name. - jobs++; - } - - if (::CreateEvent(NULL, // Default security attributes. - TRUE, // Manual Reset. - TRUE, // Object is signaled. - NULL)) { // No name. - events++; - } - } - - fprintf(output, "[GRANTED] Created %d mutexes, %d jobs and %d events for " - "a total of %d objects out of 3 000 000\r\n", mutexes, jobs, - events, mutexes + jobs + events); -} - -BOOL CALLBACK EnumWindowCallback(HWND hwnd, LPARAM output) { - DWORD pid; - ::GetWindowThreadProcessId(hwnd, &pid); - if (pid != ::GetCurrentProcessId()) { - wchar_t window_title[100 + 1] = {0}; - ::GetWindowText(hwnd, window_title, 100); - fprintf(reinterpret_cast(output), - "[GRANTED] Found window 0x%p with title %S\r\n", - hwnd, - window_title); - ::CloseWindow(hwnd); - } - - return TRUE; -} - -// Enumerates all the windows on the system and call the function to try to -// close them. The goal of this function is to try to kill the system by -// closing all windows. -// "output" is the stream used for logging. -void POCDLL_API TestCloseHWND(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - ::EnumWindows(EnumWindowCallback, PtrToLong(output)); - // TODO(nsylvain): find a way to know when the enum is finished - // before returning. - ::Sleep(3000); -} diff --git a/sandbox/sandbox_poc/pocdll/network.cc b/sandbox/sandbox_poc/pocdll/network.cc deleted file mode 100644 index 09e9f33..0000000 --- a/sandbox/sandbox_poc/pocdll/network.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/sandbox_poc/pocdll/exports.h" -#include "sandbox/sandbox_poc/pocdll/utils.h" - -// This file contains the tests used to verify the security of the network. - -void POCDLL_API TestNetworkListen(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); -#if DONT_WANT_INTERCEPTIONS_JUST_WANT_NETWORK - // Initialize Winsock - WSADATA wsa_data; - int result = ::WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (result != NO_ERROR) { - fprintf(output, "[ERROR] Cannot initialize winsock. Error%d\r\n", result); - return; - } - - // Create a SOCKET for listening for - // incoming connection requests. - SOCKET listen_socket; - listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listen_socket == INVALID_SOCKET) { - fprintf(output, "[ERROR] Failed to create socket. Error %ld\r\n", - ::WSAGetLastError()); - ::WSACleanup(); - return; - } - - // The sockaddr_in structure specifies the address family, - // IP address, and port for the socket that is being bound. - sockaddr_in service; - service.sin_family = AF_INET; - service.sin_addr.s_addr = inet_addr("127.0.0.1"); - service.sin_port = htons(88); - - if (bind(listen_socket, reinterpret_cast(&service), - sizeof(service)) == SOCKET_ERROR) { - fprintf(output, "[BLOCKED] Bind socket on port 88. Error %ld\r\n", - ::WSAGetLastError()); - closesocket(listen_socket); - ::WSACleanup(); - return; - } - - // Listen for incoming connection requests - // on the created socket - if (listen(listen_socket, SOMAXCONN) == SOCKET_ERROR) { - fprintf(output, "[BLOCKED] Listen socket on port 88. Error %ld\r\n", - ::WSAGetLastError()); - - } else { - fprintf(output, "[GRANTED] Listen socket on port 88.\r\n", - ::WSAGetLastError()); - } - - ::WSACleanup(); - return; -#else // DONT_WANT_INTERCEPTIONS_JUST_WANT_NETWORK - // Just print out that this test is not running. - fprintf(output, "[ERROR] No network tests.\r\n"); -#endif // DONT_WANT_INTERCEPTIONS_JUST_WANT_NETWORK -} diff --git a/sandbox/sandbox_poc/pocdll/pocdll.cc b/sandbox/sandbox_poc/pocdll/pocdll.cc deleted file mode 100644 index 3387064..0000000 --- a/sandbox/sandbox_poc/pocdll/pocdll.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -#include "sandbox/sandbox_poc/pocdll/exports.h" -#include "sandbox/sandbox_poc/pocdll/utils.h" - -BOOL APIENTRY DllMain(HMODULE module, - DWORD reason_for_call, - LPVOID reserved) { - UNREFERENCED_PARAMETER(module); - UNREFERENCED_PARAMETER(reason_for_call); - UNREFERENCED_PARAMETER(reserved); - return TRUE; -} - -void POCDLL_API Run(HANDLE log) { - TestFileSystem(log); - TestRegistry(log); - TestNetworkListen(log); - TestSpyScreen(log); - TestSpyKeys(log); - TestThreads(log); - TestProcesses(log); - TestGetHandle(log); -} diff --git a/sandbox/sandbox_poc/pocdll/pocdll.vcproj b/sandbox/sandbox_poc/pocdll/pocdll.vcproj deleted file mode 100644 index 8e4e31f..0000000 --- a/sandbox/sandbox_poc/pocdll/pocdll.vcproj +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sandbox/sandbox_poc/pocdll/processes_and_threads.cc b/sandbox/sandbox_poc/pocdll/processes_and_threads.cc deleted file mode 100644 index 1bef005..0000000 --- a/sandbox/sandbox_poc/pocdll/processes_and_threads.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include "sandbox/sandbox_poc/pocdll/exports.h" -#include "sandbox/sandbox_poc/pocdll/utils.h" - -// This file contains the tests used to verify the security of threads and -// processes. - -void POCDLL_API TestProcesses(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - if (INVALID_HANDLE_VALUE == snapshot) { - fprintf(output, "[BLOCKED] Cannot list all processes on the system. " - "Error %d\r\n", ::GetLastError()); - return; - } - - PROCESSENTRY32 process_entry = {0}; - process_entry.dwSize = sizeof(PROCESSENTRY32); - - BOOL result = ::Process32First(snapshot, &process_entry); - - while (result) { - HANDLE process = ::OpenProcess(PROCESS_VM_READ, - FALSE, // Do not inherit handle. - process_entry.th32ProcessID); - if (NULL == process) { - fprintf(output, "[BLOCKED] Found process %S:%d but cannot open it. " - "Error %d\r\n", - process_entry.szExeFile, - process_entry.th32ProcessID, - ::GetLastError()); - } else { - fprintf(output, "[GRANTED] Found process %S:%d and open succeeded.\r\n", - process_entry.szExeFile, process_entry.th32ProcessID); - ::CloseHandle(process); - } - - result = ::Process32Next(snapshot, &process_entry); - } - - DWORD err_code = ::GetLastError(); - if (ERROR_NO_MORE_FILES != err_code) { - fprintf(output, "[ERROR] Error %d while looking at the processes on " - "the system\r\n", err_code); - } - - ::CloseHandle(snapshot); -} - -void POCDLL_API TestThreads(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL); - if (INVALID_HANDLE_VALUE == snapshot) { - fprintf(output, "[BLOCKED] Cannot list all threads on the system. " - "Error %d\r\n", ::GetLastError()); - return; - } - - THREADENTRY32 thread_entry = {0}; - thread_entry.dwSize = sizeof(THREADENTRY32); - - BOOL result = ::Thread32First(snapshot, &thread_entry); - int nb_success = 0; - int nb_failure = 0; - - while (result) { - HANDLE thread = ::OpenThread(THREAD_QUERY_INFORMATION, - FALSE, // Do not inherit handles. - thread_entry.th32ThreadID); - if (NULL == thread) { - nb_failure++; - } else { - nb_success++; - fprintf(output, "[GRANTED] Found thread %d:%d and able to open it.\r\n", - thread_entry.th32OwnerProcessID, - thread_entry.th32ThreadID); - ::CloseHandle(thread); - } - - result = Thread32Next(snapshot, &thread_entry); - } - - DWORD err_code = ::GetLastError(); - if (ERROR_NO_MORE_FILES != err_code) { - fprintf(output, "[ERROR] Error %d while looking at the processes on " - "the system\r\n", err_code); - } - - fprintf(output, "[INFO] Found %d threads. Able to open %d of them\r\n", - nb_success + nb_failure, nb_success); - - ::CloseHandle(snapshot); -} diff --git a/sandbox/sandbox_poc/pocdll/registry.cc b/sandbox/sandbox_poc/pocdll/registry.cc deleted file mode 100644 index f5b249d..0000000 --- a/sandbox/sandbox_poc/pocdll/registry.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/sandbox_poc/pocdll/exports.h" -#include "sandbox/sandbox_poc/pocdll/utils.h" - -// This file contains the tests used to verify the security of the registry. - -// Converts an HKEY to a string. This is using the lazy way and works only -// for the main hives. -// "key" is the hive to convert to string. -// The return value is the string corresponding to the hive or "unknown" -const wchar_t *HKEYToString(const HKEY key) { - switch (reinterpret_cast(key)) { - case HKEY_CLASSES_ROOT: - return L"HKEY_CLASSES_ROOT"; - case HKEY_CURRENT_CONFIG: - return L"HKEY_CURRENT_CONFIG"; - case HKEY_CURRENT_USER: - return L"HKEY_CURRENT_USER"; - case HKEY_LOCAL_MACHINE: - return L"HKEY_LOCAL_MACHINE"; - case HKEY_USERS: - return L"HKEY_USERS"; - } - return L"unknown"; -} - -// Tries to open the key hive\path and outputs the result. -// "output" is the stream used for logging. -void TryOpenKey(const HKEY hive, const wchar_t *path, FILE *output) { - HKEY key; - LONG err_code = ::RegOpenKeyEx(hive, - path, - 0, // Reserved, must be 0. - MAXIMUM_ALLOWED, - &key); - if (ERROR_SUCCESS == err_code) { - fprintf(output, "[GRANTED] Opening key \"%S\\%S\". Handle 0x%p\r\n", - HKEYToString(hive), - path, - key); - ::RegCloseKey(key); - } else { - fprintf(output, "[BLOCKED] Opening key \"%S\\%S\". Error %d\r\n", - HKEYToString(hive), - path, - err_code); - } -} - -void POCDLL_API TestRegistry(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - TryOpenKey(HKEY_LOCAL_MACHINE, NULL, output); - TryOpenKey(HKEY_CURRENT_USER, NULL, output); - TryOpenKey(HKEY_USERS, NULL, output); - TryOpenKey(HKEY_LOCAL_MACHINE, - L"Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon", - output); -} diff --git a/sandbox/sandbox_poc/pocdll/spyware.cc b/sandbox/sandbox_poc/pocdll/spyware.cc deleted file mode 100644 index b9bf64a..0000000 --- a/sandbox/sandbox_poc/pocdll/spyware.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include "sandbox/sandbox_poc/pocdll/exports.h" -#include "sandbox/sandbox_poc/pocdll/utils.h" - -// This file contains the tests used to verify the security of the system by -// using some spying techniques. - -void POCDLL_API TestSpyKeys(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - if (RegisterHotKey(NULL, 1, 0, 0x42)) { - fprintf(output, "[GRANTED] successfully registered hotkey\r\n"); - UnregisterHotKey(NULL, 1); - } else { - fprintf(output, "[BLOCKED] Failed to register hotkey. Error = %d\r\n", - ::GetLastError()); - } - - fprintf(output, "[INFO] Logging keystrokes for 15 seconds\r\n"); - fflush(output); - std::wstring logged; - DWORD tick = ::GetTickCount() + 15000; - while (tick > ::GetTickCount()) { - for (int i = 0; i < 256; ++i) { - if (::GetAsyncKeyState(i) & 1) { - if (i >= VK_SPACE && i <= 0x5A /*VK_Z*/) { - logged.append(1, static_cast(i)); - } else { - logged.append(1, '?'); - } - } - } - } - - if (logged.size()) { - fprintf(output, "[GRANTED] Spyed keystrokes \"%S\"\r\n", - logged.c_str()); - } else { - fprintf(output, "[BLOCKED] Spyed keystrokes \"(null)\"\r\n"); - } -} - -void POCDLL_API TestSpyScreen(HANDLE log) { - HandleToFile handle2file; - FILE *output = handle2file.Translate(log, "w"); - - HDC screen_dc = ::GetDC(NULL); - COLORREF pixel_color = ::GetPixel(screen_dc, 0, 0); - - for (int x = 0; x < 10; ++x) { - for (int y = 0; y < 10; ++y) { - if (::GetPixel(screen_dc, x, y) != pixel_color) { - fprintf(output, "[GRANTED] Read pixel on screen\r\n"); - return; - } - } - } - - fprintf(output, "[BLOCKED] Read pixel on screen. Error = %d\r\n", - ::GetLastError()); -} diff --git a/sandbox/sandbox_poc/pocdll/utils.h b/sandbox/sandbox_poc/pocdll/utils.h deleted file mode 100644 index ae42861..0000000 --- a/sandbox/sandbox_poc/pocdll/utils.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SANDBOX_POC_POCDLL_UTILS_H__ -#define SANDBOX_SANDBOX_POC_POCDLL_UTILS_H__ - -#include -#include -#include "base/basictypes.h" - -// Class to convert a HANDLE to a FILE *. The FILE * is closed when the -// object goes out of scope -class HandleToFile { - public: - HandleToFile() { - file_ = NULL; - }; - - // Note: c_file_handle_ does not need to be closed because fclose does it. - ~HandleToFile() { - if (file_) { - fflush(file_); - fclose(file_); - } - }; - - // Translates a HANDLE (handle) to a FILE * opened with the mode "mode". - // The return value is the FILE * or NULL if there is an error. - FILE* Translate(HANDLE handle, const char *mode) { - if (file_) { - return NULL; - } - - HANDLE new_handle; - BOOL result = ::DuplicateHandle(::GetCurrentProcess(), - handle, - ::GetCurrentProcess(), - &new_handle, - 0, // Don't ask for a specific - // desired access. - FALSE, // Not inheritable. - DUPLICATE_SAME_ACCESS); - - if (!result) { - return NULL; - } - - int c_file_handle = _open_osfhandle(reinterpret_cast(new_handle), - 0); // No flags - if (-1 == c_file_handle) { - return NULL; - } - - file_ = _fdopen(c_file_handle, mode); - return file_; - }; - private: - // the FILE* returned. We need to closed it at the end. - FILE* file_; - - DISALLOW_COPY_AND_ASSIGN(HandleToFile); -}; - -#endif // SANDBOX_SANDBOX_POC_POCDLL_UTILS_H__ diff --git a/sandbox/sandbox_poc/resource.h b/sandbox/sandbox_poc/resource.h deleted file mode 100644 index 87ff920..0000000 --- a/sandbox/sandbox_poc/resource.h +++ /dev/null @@ -1,30 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by sandbox.rc -// -#define IDI_SANDBOX 107 -#define IDR_MENU_MAIN_UI 129 -#define IDD_LAUNCH_DLL 130 -#define IDC_RADIO_POCDLL 1000 -#define IDC_RADIO_CUSTOM_DLL 1001 -#define IDC_DLL_NAME 1002 -#define IDC_ENTRY_POINT 1003 -#define IDC_LOG_FILE 1004 -#define IDC_BROWSE_DLL 1005 -#define IDC_BROWSE_LOG 1006 -#define ID_FILE_EXIT 32771 -#define ID_COMMANDS_LAUNCHDLL 32772 -#define ID_COMMANDS_SPAWNTARGET 32773 -#define IDC_STATIC -1 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 131 -#define _APS_NEXT_COMMAND_VALUE 32774 -#define _APS_NEXT_CONTROL_VALUE 1007 -#define _APS_NEXT_SYMED_VALUE 110 -#endif -#endif diff --git a/sandbox/sandbox_poc/sandbox.cc b/sandbox/sandbox_poc/sandbox.cc deleted file mode 100644 index 4dc0882..0000000 --- a/sandbox/sandbox_poc/sandbox.cc +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include -#include "sandbox/sandbox_poc/sandbox.h" -#include "base/logging.h" -#include "sandbox/sandbox_poc/main_ui_window.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_factory.h" - -// Prototype allowed for functions to be called in the POC -typedef void(__cdecl *lpfnInit)(HANDLE); - -bool ParseCommandLine(wchar_t * command_line, - std::string * dll_name, - std::string * entry_point, - std::wstring * log_file) { - DCHECK(dll_name); - DCHECK(entry_point); - DCHECK(log_file); - if (!dll_name || !entry_point || !log_file) - return false; - - LPWSTR *arg_list; - int arg_count; - - // We expect the command line to contain: EntryPointName "DLLPath" "LogPath" - // NOTE: Double quotes are required, even if long path name not used - // NOTE: LogPath can be blank, but still requires the double quotes - arg_list = CommandLineToArgvW(command_line, &arg_count); - if (NULL == arg_list || arg_count < 4) { - return false; - } - - std::wstring entry_point_wide = arg_list[1]; - std::wstring dll_name_wide = arg_list[2]; - *entry_point = std::string(entry_point_wide.begin(), entry_point_wide.end()); - *dll_name = std::string(dll_name_wide.begin(), dll_name_wide.end()); - *log_file = arg_list[3]; - - // Free memory allocated for CommandLineToArgvW arguments. - LocalFree(arg_list); - - return true; -} - -int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, wchar_t* command_line, - int show_command) { - UNREFERENCED_PARAMETER(command_line); - - sandbox::BrokerServices* broker_service = - sandbox::SandboxFactory::GetBrokerServices(); - sandbox::ResultCode result; - - // This application starts as the broker; an application with a UI that - // spawns an instance of itself (called a 'target') inside the sandbox. - // Before spawning a hidden instance of itself, the application will have - // asked the user which DLL the spawned instance should load and passes - // that as command line argument to the spawned instance. - // - // We check here to see if we can retrieve a pointer to the BrokerServices, - // which is not possible if we are running inside the sandbox under a - // restricted token so it also tells us which mode we are in. If we can - // retrieve the pointer, then we are the broker, otherwise we are the target - // that the broker launched. - if (NULL != broker_service) { - // Yes, we are the broker so we need to initialize and show the UI - if (0 != (result = broker_service->Init())) { - ::MessageBox(NULL, L"Failed to initialize the BrokerServices object", - L"Error during initialization", MB_ICONERROR); - return 1; - } - - wchar_t exe_name[MAX_PATH]; - if (0 == GetModuleFileName(NULL, exe_name, MAX_PATH - 1)) { - ::MessageBox(NULL, L"Failed to get name of current EXE", - L"Error during initialization", MB_ICONERROR); - return 1; - } - - // The CreateMainWindowAndLoop() call will not return until the user closes - // the application window (or selects File\Exit). - MainUIWindow window; - window.CreateMainWindowAndLoop(instance, - exe_name, - show_command, - broker_service); - - - // Cannot exit until we have cleaned up after all the targets we have - // created - broker_service->WaitForAllTargets(); - } else { - // This is an instance that has been spawned inside the sandbox by the - // broker, so we need to parse the command line to figure out which DLL to - // load and what entry point to call - sandbox::TargetServices* target_service - = sandbox::SandboxFactory::GetTargetServices(); - - if (NULL == target_service) { - // TODO(finnur): write the failure to the log file - // We cannot display messageboxes inside the sandbox unless access to - // the desktop handle has been granted to us, and we don't have a - // console window to write to. Therefore we need to have the broker - // grant us access to a handle to a logfile and write the error that - // occurred into the log before continuing - return -1; - } - - // Debugging the spawned application can be tricky, because DebugBreak() - // and _asm int 3 cause the app to terminate (due to a flag in the job - // object), MessageBoxes() will not be displayed unless we have been granted - // that privilege and the target finishes its business so quickly we cannot - // attach to it quickly enough. Therefore, you can uncomment the - // following line and attach (w. msdev or windbg) as the target is sleeping - - // Sleep(10000); - - if (sandbox::SBOX_ALL_OK != (result = target_service->Init())) { - // TODO(finnur): write the initialization error to the log file - return -2; - } - - // Parse the command line to find out what we need to call - std::string dll_name, entry_point; - std::wstring log_file; - if (!ParseCommandLine(GetCommandLineW(), - &dll_name, - &entry_point, - &log_file)) { - // TODO(finnur): write the failure to the log file - return -3; - } - - // Open the pipe to transfert the log output - HANDLE pipe = ::CreateFile(log_file.c_str(), - GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, // Default security attributes. - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); // No template - - if (INVALID_HANDLE_VALUE == pipe) { - return -4; - } - - // We now know what we should load, so load it - HMODULE dll_module = ::LoadLibraryA(dll_name.c_str()); - if (dll_module == NULL) { - // TODO(finnur): write the failure to the log file - return -5; - } - - // Initialization is finished, so we can enter lock-down mode - target_service->LowerToken(); - - lpfnInit init_function = - (lpfnInit) ::GetProcAddress(dll_module, entry_point.c_str()); - - if (!init_function) { - // TODO(finnur): write the failure to the log file - ::FreeLibrary(dll_module); - CloseHandle(pipe); - return -6; - } - - // Transfer control to the entry point in the DLL requested - init_function(pipe); - - CloseHandle(pipe); - Sleep(1000); // Give a change to the debug output to arrive before the - // end of the process - - ::FreeLibrary(dll_module); - } - - return 0; -} diff --git a/sandbox/sandbox_poc/sandbox.h b/sandbox/sandbox_poc/sandbox.h deleted file mode 100644 index 15531ad..0000000 --- a/sandbox/sandbox_poc/sandbox.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SANDBOX_POC_SANDBOX_H__ -#define SANDBOX_SANDBOX_POC_SANDBOX_H__ - -#include "sandbox/sandbox_poc/resource.h" - -#endif // SANDBOX_SANDBOX_POC_SANDBOX_H__ diff --git a/sandbox/sandbox_poc/sandbox.ico b/sandbox/sandbox_poc/sandbox.ico deleted file mode 100644 index 916fa12..0000000 Binary files a/sandbox/sandbox_poc/sandbox.ico and /dev/null differ diff --git a/sandbox/sandbox_poc/sandbox.rc b/sandbox/sandbox_poc/sandbox.rc deleted file mode 100644 index 978c96f..0000000 --- a/sandbox/sandbox_poc/sandbox.rc +++ /dev/null @@ -1,136 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#define APSTUDIO_HIDDEN_SYMBOLS -#include "windows.h" -#undef APSTUDIO_HIDDEN_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDR_MENU_MAIN_UI MENU -BEGIN - POPUP "&File" - BEGIN - MENUITEM "E&xit", ID_FILE_EXIT - END - POPUP "&Commands" - BEGIN - MENUITEM "&Spawn target", ID_COMMANDS_SPAWNTARGET - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_LAUNCH_DLL DIALOGEX 0, 0, 269, 118 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "BrokerUI: Load an Attack DLL" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "Call now",IDOK,212,70,50,14 - PUSHBUTTON "Cancel",IDCANCEL,212,95,50,14 - EDITTEXT IDC_DLL_NAME,7,43,200,13,ES_AUTOHSCROLL - LTEXT "DLL to load in target:",IDC_STATIC,7,33,168,8 - LTEXT "Function to call:",IDC_STATIC,7,61,139,8 - EDITTEXT IDC_ENTRY_POINT,7,71,200,13,ES_AUTOHSCROLL - EDITTEXT IDC_LOG_FILE,7,17,200,13,ES_AUTOHSCROLL - LTEXT "File for Target logging (optional):",IDC_STATIC,7,7,139,8 - PUSHBUTTON "Browse...",IDC_BROWSE_DLL,212,42,50,14 - PUSHBUTTON "Browse...",IDC_BROWSE_LOG,212,16,50,14 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_LAUNCH_DLL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 262 - TOPMARGIN, 7 - BOTTOMMARGIN, 111 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_SANDBOX ICON "sandbox.ico" - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" - "#include ""windows.h""\r\n" - "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/sandbox/sandbox_poc/sandbox_poc.vcproj b/sandbox/sandbox_poc/sandbox_poc.vcproj deleted file mode 100644 index 5fde1cd..0000000 --- a/sandbox/sandbox_poc/sandbox_poc.vcproj +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sandbox/sandbox_standalone.sln b/sandbox/sandbox_standalone.sln deleted file mode 100644 index 529d20e..0000000 --- a/sandbox/sandbox_standalone.sln +++ /dev/null @@ -1,127 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox", "src\sandbox.vcproj", "{881F6A97-D539-4C48-B401-DF04385B2343}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sbox_unittests", "tests\unit_tests\sbox_unittests.vcproj", "{883553BE-2A9D-418C-A121-61FE1DFBC562}" - ProjectSection(ProjectDependencies) = postProject - {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} - {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} - {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sbox_validation_tests", "tests\validation_tests\sbox_validation_tests.vcproj", "{B9CC7B0D-145A-49C2-B887-84E43CFA0F27}" - ProjectSection(ProjectDependencies) = postProject - {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} - {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} - {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dependencies", "dependencies", "{BCE54389-D18D-48B9-977E-9D1998200F63}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debug_message", "..\base\debug_message.vcproj", "{F0F92189-193A-6607-C2BB-0F98BBD19ADF}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{7F36EE20-5016-4051-B0D7-42824CDA0291}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "proof_of_concept", "proof_of_concept", "{B607BE7B-3555-422C-A40B-28E73C0B5E24}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox_poc", "sandbox_poc\sandbox_poc.vcproj", "{CF757839-F2A1-417C-8F25-DCAE480020F1}" - ProjectSection(ProjectDependencies) = postProject - {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} - {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} - {AE5BFB87-850E-4454-B01D-58E7D8BAC224} = {AE5BFB87-850E-4454-B01D-58E7D8BAC224} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pocdll", "sandbox_poc\pocdll\pocdll.vcproj", "{AE5BFB87-850E-4454-B01D-58E7D8BAC224}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "finder", "tools\finder\finder.vcproj", "{ACDC2E06-0366-41A4-A646-C37E130A605D}" - ProjectSection(ProjectDependencies) = postProject - {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} - {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "launcher", "tools\launcher\launcher.vcproj", "{386FA217-FBC2-4461-882D-CDAD221ED800}" - ProjectSection(ProjectDependencies) = postProject - {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} - {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sbox_integration_tests", "tests\integration_tests\sbox_integration_tests.vcproj", "{542D4B3B-98D4-4233-B68D-0103891508C6}" - ProjectSection(ProjectDependencies) = postProject - {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} - {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} - {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base", "..\base\base.vcproj", "{1832A374-8A74-4F9E-B536-69A699B3E165}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest", "..\testing\gtest.vcproj", "{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {881F6A97-D539-4C48-B401-DF04385B2343}.Debug|Win32.ActiveCfg = Debug|Win32 - {881F6A97-D539-4C48-B401-DF04385B2343}.Debug|Win32.Build.0 = Debug|Win32 - {881F6A97-D539-4C48-B401-DF04385B2343}.Release|Win32.ActiveCfg = Release|Win32 - {881F6A97-D539-4C48-B401-DF04385B2343}.Release|Win32.Build.0 = Release|Win32 - {883553BE-2A9D-418C-A121-61FE1DFBC562}.Debug|Win32.ActiveCfg = Debug|Win32 - {883553BE-2A9D-418C-A121-61FE1DFBC562}.Debug|Win32.Build.0 = Debug|Win32 - {883553BE-2A9D-418C-A121-61FE1DFBC562}.Release|Win32.ActiveCfg = Release|Win32 - {883553BE-2A9D-418C-A121-61FE1DFBC562}.Release|Win32.Build.0 = Release|Win32 - {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Debug|Win32.ActiveCfg = Debug|Win32 - {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Debug|Win32.Build.0 = Debug|Win32 - {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Release|Win32.ActiveCfg = Release|Win32 - {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Release|Win32.Build.0 = Release|Win32 - {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Debug|Win32.ActiveCfg = Debug|Win32 - {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Debug|Win32.Build.0 = Debug|Win32 - {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Release|Win32.ActiveCfg = Release|Win32 - {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Release|Win32.Build.0 = Release|Win32 - {CF757839-F2A1-417C-8F25-DCAE480020F1}.Debug|Win32.ActiveCfg = Debug|Win32 - {CF757839-F2A1-417C-8F25-DCAE480020F1}.Debug|Win32.Build.0 = Debug|Win32 - {CF757839-F2A1-417C-8F25-DCAE480020F1}.Release|Win32.ActiveCfg = Release|Win32 - {CF757839-F2A1-417C-8F25-DCAE480020F1}.Release|Win32.Build.0 = Release|Win32 - {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Debug|Win32.ActiveCfg = Debug|Win32 - {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Debug|Win32.Build.0 = Debug|Win32 - {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Release|Win32.ActiveCfg = Release|Win32 - {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Release|Win32.Build.0 = Release|Win32 - {ACDC2E06-0366-41A4-A646-C37E130A605D}.Debug|Win32.ActiveCfg = Debug|Win32 - {ACDC2E06-0366-41A4-A646-C37E130A605D}.Debug|Win32.Build.0 = Debug|Win32 - {ACDC2E06-0366-41A4-A646-C37E130A605D}.Release|Win32.ActiveCfg = Release|Win32 - {ACDC2E06-0366-41A4-A646-C37E130A605D}.Release|Win32.Build.0 = Release|Win32 - {386FA217-FBC2-4461-882D-CDAD221ED800}.Debug|Win32.ActiveCfg = Debug|Win32 - {386FA217-FBC2-4461-882D-CDAD221ED800}.Debug|Win32.Build.0 = Debug|Win32 - {386FA217-FBC2-4461-882D-CDAD221ED800}.Release|Win32.ActiveCfg = Release|Win32 - {386FA217-FBC2-4461-882D-CDAD221ED800}.Release|Win32.Build.0 = Release|Win32 - {542D4B3B-98D4-4233-B68D-0103891508C6}.Debug|Win32.ActiveCfg = Debug|Win32 - {542D4B3B-98D4-4233-B68D-0103891508C6}.Debug|Win32.Build.0 = Debug|Win32 - {542D4B3B-98D4-4233-B68D-0103891508C6}.Release|Win32.ActiveCfg = Release|Win32 - {542D4B3B-98D4-4233-B68D-0103891508C6}.Release|Win32.Build.0 = Release|Win32 - {1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.ActiveCfg = Debug|Win32 - {1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.Build.0 = Debug|Win32 - {1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.ActiveCfg = Release|Win32 - {1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.Build.0 = Release|Win32 - {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.ActiveCfg = Debug|Win32 - {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.Build.0 = Debug|Win32 - {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.ActiveCfg = Release|Win32 - {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {883553BE-2A9D-418C-A121-61FE1DFBC562} = {F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED} - {B9CC7B0D-145A-49C2-B887-84E43CFA0F27} = {F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED} - {542D4B3B-98D4-4233-B68D-0103891508C6} = {F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED} - {F0F92189-193A-6607-C2BB-0F98BBD19ADF} = {BCE54389-D18D-48B9-977E-9D1998200F63} - {1832A374-8A74-4F9E-B536-69A699B3E165} = {BCE54389-D18D-48B9-977E-9D1998200F63} - {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BCE54389-D18D-48B9-977E-9D1998200F63} - {ACDC2E06-0366-41A4-A646-C37E130A605D} = {7F36EE20-5016-4051-B0D7-42824CDA0291} - {386FA217-FBC2-4461-882D-CDAD221ED800} = {7F36EE20-5016-4051-B0D7-42824CDA0291} - {CF757839-F2A1-417C-8F25-DCAE480020F1} = {B607BE7B-3555-422C-A40B-28E73C0B5E24} - {AE5BFB87-850E-4454-B01D-58E7D8BAC224} = {B607BE7B-3555-422C-A40B-28E73C0B5E24} - EndGlobalSection -EndGlobal diff --git a/sandbox/sandbox_win.gypi b/sandbox/sandbox_win.gypi deleted file mode 100644 index 86506c1..0000000 --- a/sandbox/sandbox_win.gypi +++ /dev/null @@ -1,342 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'target_defaults': { - 'variables': { - 'sandbox_windows_target': 0, - }, - 'target_conditions': [ - ['sandbox_windows_target==1', { - # Files that are shared between the 32-bit and the 64-bit versions - # of the Windows sandbox library. - 'sources': [ - 'src/acl.cc', - 'src/acl.h', - 'src/broker_services.cc', - 'src/broker_services.h', - 'src/crosscall_client.h', - 'src/crosscall_params.h', - 'src/crosscall_server.cc', - 'src/crosscall_server.h', - 'src/dep.cc', - 'src/dep.h', - 'src/eat_resolver.cc', - 'src/eat_resolver.h', - 'src/filesystem_dispatcher.cc', - 'src/filesystem_dispatcher.h', - 'src/filesystem_interception.cc', - 'src/filesystem_interception.h', - 'src/filesystem_policy.cc', - 'src/filesystem_policy.h', - 'src/handle_closer.cc', - 'src/handle_closer.h', - 'src/handle_closer_agent.cc', - 'src/handle_closer_agent.h', - 'src/handle_dispatcher.cc', - 'src/handle_dispatcher.h', - 'src/handle_interception.cc', - 'src/handle_interception.h', - 'src/handle_policy.cc', - 'src/handle_policy.h', - 'src/handle_table.cc', - 'src/handle_table.h', - 'src/interception.cc', - 'src/interception.h', - 'src/interception_agent.cc', - 'src/interception_agent.h', - 'src/interception_internal.h', - 'src/interceptors.h', - 'src/internal_types.h', - 'src/ipc_tags.h', - 'src/job.cc', - 'src/job.h', - 'src/named_pipe_dispatcher.cc', - 'src/named_pipe_dispatcher.h', - 'src/named_pipe_interception.cc', - 'src/named_pipe_interception.h', - 'src/named_pipe_policy.cc', - 'src/named_pipe_policy.h', - 'src/nt_internals.h', - 'src/policy_broker.cc', - 'src/policy_broker.h', - 'src/policy_engine_opcodes.cc', - 'src/policy_engine_opcodes.h', - 'src/policy_engine_params.h', - 'src/policy_engine_processor.cc', - 'src/policy_engine_processor.h', - 'src/policy_low_level.cc', - 'src/policy_low_level.h', - 'src/policy_params.h', - 'src/policy_target.cc', - 'src/policy_target.h', - 'src/process_thread_dispatcher.cc', - 'src/process_thread_dispatcher.h', - 'src/process_thread_interception.cc', - 'src/process_thread_interception.h', - 'src/process_thread_policy.cc', - 'src/process_thread_policy.h', - 'src/registry_dispatcher.cc', - 'src/registry_dispatcher.h', - 'src/registry_interception.cc', - 'src/registry_interception.h', - 'src/registry_policy.cc', - 'src/registry_policy.h', - 'src/resolver.cc', - 'src/resolver.h', - 'src/restricted_token_utils.cc', - 'src/restricted_token_utils.h', - 'src/restricted_token.cc', - 'src/restricted_token.h', - 'src/sandbox_factory.h', - 'src/sandbox_nt_types.h', - 'src/sandbox_nt_util.cc', - 'src/sandbox_nt_util.h', - 'src/sandbox_policy_base.cc', - 'src/sandbox_policy_base.h', - 'src/sandbox_policy.h', - 'src/sandbox_types.h', - 'src/sandbox_utils.cc', - 'src/sandbox_utils.h', - 'src/sandbox.cc', - 'src/sandbox.h', - 'src/security_level.h', - 'src/service_resolver.cc', - 'src/service_resolver.h', - 'src/shared_handles.cc', - 'src/shared_handles.h', - 'src/sharedmem_ipc_client.cc', - 'src/sharedmem_ipc_client.h', - 'src/sharedmem_ipc_server.cc', - 'src/sharedmem_ipc_server.h', - 'src/sid.cc', - 'src/sid.h', - 'src/sync_dispatcher.cc', - 'src/sync_dispatcher.h', - 'src/sync_interception.cc', - 'src/sync_interception.h', - 'src/sync_policy.cc', - 'src/sync_policy.h', - 'src/target_interceptions.cc', - 'src/target_interceptions.h', - 'src/target_process.cc', - 'src/target_process.h', - 'src/target_services.cc', - 'src/target_services.h', - 'src/win_utils.cc', - 'src/win_utils.h', - 'src/win2k_threadpool.cc', - 'src/win2k_threadpool.h', - 'src/window.cc', - 'src/window.h', - ], - }], - ], - }, - 'targets': [ - { - 'target_name': 'sandbox', - 'type': 'static_library', - 'variables': { - 'sandbox_windows_target': 1, - }, - 'dependencies': [ - '../testing/gtest.gyp:gtest', - '../base/base.gyp:base', - '../base/base.gyp:base_static', - ], - 'export_dependent_settings': [ - '../base/base.gyp:base', - ], - 'sources': [ - # Files that are used by the 32-bit version of Windows sandbox only. - 'src/resolver_32.cc', - 'src/service_resolver_32.cc', - 'src/sidestep_resolver.cc', - 'src/sidestep_resolver.h', - 'src/sidestep\ia32_modrm_map.cpp', - 'src/sidestep\ia32_opcode_map.cpp', - 'src/sidestep\mini_disassembler_types.h', - 'src/sidestep\mini_disassembler.cpp', - 'src/sidestep\mini_disassembler.h', - 'src/sidestep\preamble_patcher_with_stub.cpp', - 'src/sidestep\preamble_patcher.h', - 'src/Wow64.cc', - 'src/Wow64.h', - ], - 'include_dirs': [ - '..', - ], - 'copies': [ - { - 'destination': '<(PRODUCT_DIR)', - 'files': [ - 'wow_helper/wow_helper.exe', - 'wow_helper/wow_helper.pdb', - ], - }, - ], - 'direct_dependent_settings': { - 'include_dirs': [ - 'src', - '..', - ], - }, - }, - { - 'target_name': 'sandbox_win64', - 'type': 'static_library', - 'variables': { - 'sandbox_windows_target': 1, - }, - 'dependencies': [ - '../testing/gtest.gyp:gtest', - '../base/base.gyp:base_nacl_win64', - '../base/base.gyp:base_static_win64', - ], - 'configurations': { - 'Common_Base': { - 'msvs_target_platform': 'x64', - }, - }, - 'sources': [ - # Files that are used by the 64-bit version of Windows sandbox only. - 'src/interceptors_64.cc', - 'src/interceptors_64.h', - 'src/resolver_64.cc', - 'src/service_resolver_64.cc', - 'src/Wow64_64.cc', - ], - 'include_dirs': [ - '..', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - 'src', - '..', - ], - }, - 'defines': [ - '<@(nacl_win64_defines)', - ] - }, - { - 'target_name': 'sbox_integration_tests', - 'type': 'executable', - 'dependencies': [ - 'sandbox', - '../testing/gtest.gyp:gtest', - ], - 'sources': [ - 'tests/common/controller.cc', - 'tests/common/controller.h', - 'tests/common/test_utils.cc', - 'tests/common/test_utils.h', - 'tests/integration_tests/integration_tests.cc', - 'src/dep_test.cc', - 'src/file_policy_test.cc', - 'src/handle_policy_test.cc', - 'tests/integration_tests/integration_tests_test.cc', - 'src/handle_closer_test.cc', - 'src/integrity_level_test.cc', - 'src/ipc_ping_test.cc', - 'src/named_pipe_policy_test.cc', - 'src/policy_target_test.cc', - 'src/process_policy_test.cc', - 'src/registry_policy_test.cc', - 'src/sync_policy_test.cc', - 'src/unload_dll_test.cc', - ], - }, - { - 'target_name': 'sbox_validation_tests', - 'type': 'executable', - 'dependencies': [ - 'sandbox', - '../testing/gtest.gyp:gtest', - ], - 'sources': [ - 'tests/common/controller.cc', - 'tests/common/controller.h', - 'tests/validation_tests/unit_tests.cc', - 'tests/validation_tests/commands.cc', - 'tests/validation_tests/commands.h', - 'tests/validation_tests/suite.cc', - ], - }, - { - 'target_name': 'sbox_unittests', - 'type': 'executable', - 'dependencies': [ - 'sandbox', - '../testing/gtest.gyp:gtest', - ], - 'sources': [ - 'tests/common/test_utils.cc', - 'tests/common/test_utils.h', - 'tests/unit_tests/unit_tests.cc', - 'src/interception_unittest.cc', - 'src/service_resolver_unittest.cc', - 'src/restricted_token_unittest.cc', - 'src/job_unittest.cc', - 'src/sid_unittest.cc', - 'src/policy_engine_unittest.cc', - 'src/policy_low_level_unittest.cc', - 'src/policy_opcodes_unittest.cc', - 'src/ipc_unittest.cc', - 'src/threadpool_unittest.cc', - 'src/win_utils_unittest.cc', - ], - }, - { - 'target_name': 'sandbox_poc', - 'type': 'executable', - 'dependencies': [ - 'sandbox', - 'pocdll', - ], - 'sources': [ - 'sandbox_poc/main_ui_window.cc', - 'sandbox_poc/main_ui_window.h', - 'sandbox_poc/resource.h', - 'sandbox_poc/sandbox.cc', - 'sandbox_poc/sandbox.h', - 'sandbox_poc/sandbox.ico', - 'sandbox_poc/sandbox.rc', - ], - 'link_settings': { - 'libraries': [ - '-lcomctl32.lib', - ], - }, - 'msvs_settings': { - 'VCLinkerTool': { - 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS - }, - }, - }, - { - 'target_name': 'pocdll', - 'type': 'shared_library', - 'sources': [ - 'sandbox_poc/pocdll/exports.h', - 'sandbox_poc/pocdll/fs.cc', - 'sandbox_poc/pocdll/handles.cc', - 'sandbox_poc/pocdll/invasive.cc', - 'sandbox_poc/pocdll/network.cc', - 'sandbox_poc/pocdll/pocdll.cc', - 'sandbox_poc/pocdll/processes_and_threads.cc', - 'sandbox_poc/pocdll/registry.cc', - 'sandbox_poc/pocdll/spyware.cc', - 'sandbox_poc/pocdll/utils.h', - ], - 'defines': [ - 'POCDLL_EXPORTS', - ], - 'include_dirs': [ - '..', - ], - }, - ], -} diff --git a/sandbox/src/Wow64.cc b/sandbox/src/Wow64.cc deleted file mode 100644 index 5098647..0000000 --- a/sandbox/src/Wow64.cc +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/wow64.h" - -#include - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/win/scoped_process_information.h" -#include "base/win/windows_version.h" -#include "sandbox/src/target_process.h" - -namespace { - -// Holds the information needed for the interception of NtMapViewOfSection on -// 64 bits. -// Warning: do not modify this definition without changing also the code on the -// 64 bit helper process. -struct PatchInfo32 { - HANDLE dll_load; // Event to signal the broker. - ULONG pad1; - HANDLE continue_load; // Event to wait for the broker. - ULONG pad2; - HANDLE section; // First argument of the call. - ULONG pad3; - void* orig_MapViewOfSection; - ULONG original_high; - void* signal_and_wait; - ULONG pad4; - void* patch_location; - ULONG patch_high; -}; - -// Size of the 64 bit service entry. -const SIZE_T kServiceEntry64Size = 0x10; - -// Removes the interception of ntdll64. -bool Restore64Code(HANDLE child, PatchInfo32* patch_info) { - PatchInfo32 local_patch_info; - SIZE_T actual; - if (!::ReadProcessMemory(child, patch_info, &local_patch_info, - sizeof(local_patch_info), &actual)) - return false; - if (sizeof(local_patch_info) != actual) - return false; - - if (local_patch_info.original_high) - return false; - if (local_patch_info.patch_high) - return false; - - char buffer[kServiceEntry64Size]; - - if (!::ReadProcessMemory(child, local_patch_info.orig_MapViewOfSection, - &buffer, kServiceEntry64Size, &actual)) - return false; - if (kServiceEntry64Size != actual) - return false; - - if (!::WriteProcessMemory(child, local_patch_info.patch_location, &buffer, - kServiceEntry64Size, &actual)) - return false; - if (kServiceEntry64Size != actual) - return false; - return true; -} - -typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64); - -} // namespace - -namespace sandbox { - -Wow64::~Wow64() { - if (dll_load_) - ::CloseHandle(dll_load_); - - if (continue_load_) - ::CloseHandle(continue_load_); -} - -// The basic idea is to allocate one page of memory on the child, and initialize -// the first part of it with our version of PatchInfo32. Then launch the helper -// process passing it that address on the child. The helper process will patch -// the 64 bit version of NtMapViewOfFile, and the interception will signal the -// first event on the buffer. We'll be waiting on that event and after the 32 -// bit version of ntdll is loaded, we'll remove the interception and return to -// our caller. -bool Wow64::WaitForNtdll() { - if (base::win::OSInfo::GetInstance()->wow64_status() != - base::win::OSInfo::WOW64_ENABLED) - return true; - - const size_t page_size = 4096; - - // Create some default manual reset un-named events, not signaled. - dll_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL); - continue_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL); - HANDLE current_process = ::GetCurrentProcess(); - HANDLE remote_load, remote_continue; - DWORD access = EVENT_MODIFY_STATE | SYNCHRONIZE; - if (!::DuplicateHandle(current_process, dll_load_, child_->Process(), - &remote_load, access, FALSE, 0)) - return false; - if (!::DuplicateHandle(current_process, continue_load_, child_->Process(), - &remote_continue, access, FALSE, 0)) - return false; - - void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size, - MEM_COMMIT, PAGE_EXECUTE_READWRITE); - DCHECK(buffer); - if (!buffer) - return false; - - PatchInfo32* patch_info = reinterpret_cast(buffer); - PatchInfo32 local_patch_info = {0}; - local_patch_info.dll_load = remote_load; - local_patch_info.continue_load = remote_continue; - SIZE_T written; - if (!::WriteProcessMemory(child_->Process(), patch_info, &local_patch_info, - offsetof(PatchInfo32, section), &written)) - return false; - if (offsetof(PatchInfo32, section) != written) - return false; - - if (!RunWowHelper(buffer)) - return false; - - // The child is intercepted on 64 bit, go on and wait for our event. - if (!DllMapped()) - return false; - - // The 32 bit version is available, cleanup the child. - return Restore64Code(child_->Process(), patch_info); -} - -bool Wow64::RunWowHelper(void* buffer) { - COMPILE_ASSERT(sizeof(buffer) <= sizeof(DWORD), unsupported_64_bits); - - // Get the path to the helper (beside the exe). - wchar_t prog_name[MAX_PATH]; - GetModuleFileNameW(NULL, prog_name, MAX_PATH); - std::wstring path(prog_name); - size_t name_pos = path.find_last_of(L"\\"); - if (std::wstring::npos == name_pos) - return false; - path.resize(name_pos + 1); - - std::wstringstream command; - command << std::hex << std::showbase << L"\"" << path << - L"wow_helper.exe\" " << child_->ProcessId() << " " << - bit_cast(buffer); - - scoped_ptr_malloc writable_command(_wcsdup(command.str().c_str())); - - STARTUPINFO startup_info = {0}; - startup_info.cb = sizeof(startup_info); - base::win::ScopedProcessInformation process_info; - if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL, - NULL, &startup_info, process_info.Receive())) - return false; - - DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE); - - DWORD code; - bool ok = - ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false; - - if (WAIT_TIMEOUT == reason) - return false; - - return ok && (0 == code); -} - -// First we must wake up the child, then wait for dll loads on the child until -// the one we care is loaded; at that point we must suspend the child again. -bool Wow64::DllMapped() { - if (1 != ::ResumeThread(child_->MainThread())) { - NOTREACHED(); - return false; - } - - for (;;) { - DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE); - if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason) - return false; - - if (!::ResetEvent(dll_load_)) - return false; - - bool found = NtdllPresent(); - if (found) { - if (::SuspendThread(child_->MainThread())) - return false; - } - - if (!::SetEvent(continue_load_)) - return false; - - if (found) - return true; - } -} - -bool Wow64::NtdllPresent() { - const size_t kBufferSize = 512; - char buffer[kBufferSize]; - SIZE_T read; - if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize, - &read)) - return false; - if (kBufferSize != read) - return false; - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/Wow64.h b/sandbox/src/Wow64.h deleted file mode 100644 index 472297e..0000000 --- a/sandbox/src/Wow64.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_WOW64_H__ -#define SANDBOX_SRC_WOW64_H__ - -#include - -#include "base/basictypes.h" -#include "sandbox/src/sandbox_types.h" - -namespace sandbox { - -class TargetProcess; - -// This class wraps the code needed to interact with the Windows On Windows -// subsystem on 64 bit OSes, from the point of view of interceptions. -class Wow64 { - public: - Wow64(TargetProcess* child, HMODULE ntdll) - : child_(child), ntdll_(ntdll), dll_load_(NULL), continue_load_(NULL) {} - ~Wow64(); - - // Waits for the 32 bit DLL to get loaded on the child process. This function - // will return immediately if not running under WOW, or launch the helper - // process and wait until ntdll is ready. - bool WaitForNtdll(); - - private: - // Runs the WOW helper process, passing the address of a buffer allocated on - // the child (one page). - bool RunWowHelper(void* buffer); - - // This method receives "notifications" whenever a DLL is mapped on the child. - bool DllMapped(); - - // Returns true if ntdll.dll is mapped on the child. - bool NtdllPresent(); - - TargetProcess* child_; // Child process. - HMODULE ntdll_; // ntdll on the parent. - HANDLE dll_load_; // Event that is signaled on dll load. - HANDLE continue_load_; // Event to signal to continue execution on the child. - DISALLOW_IMPLICIT_CONSTRUCTORS(Wow64); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_WOW64_H__ diff --git a/sandbox/src/Wow64_64.cc b/sandbox/src/Wow64_64.cc deleted file mode 100644 index 5218077..0000000 --- a/sandbox/src/Wow64_64.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Wow64 implementation for native 64-bit Windows (in other words, never WOW). - -#include "sandbox/src/wow64.h" - -namespace sandbox { - -Wow64::~Wow64() { -} - -bool Wow64::WaitForNtdll() { - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/acl.cc b/sandbox/src/acl.cc deleted file mode 100644 index 4869bb0..0000000 --- a/sandbox/src/acl.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/acl.h" - -#include -#include - -#include "base/logging.h" - -namespace sandbox { - -bool GetDefaultDacl(HANDLE token, - scoped_ptr_malloc* default_dacl) { - if (token == NULL) - return false; - - DCHECK(default_dacl != NULL); - - unsigned long length = 0; - ::GetTokenInformation(token, TokenDefaultDacl, NULL, 0, &length); - if (length == 0) { - NOTREACHED(); - return false; - } - - TOKEN_DEFAULT_DACL* acl = - reinterpret_cast(malloc(length)); - default_dacl->reset(acl); - - if (!::GetTokenInformation(token, TokenDefaultDacl, default_dacl->get(), - length, &length)) - return false; - - return true; -} - -bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MASK access, - ACL** new_dacl) { - EXPLICIT_ACCESS new_access = {0}; - new_access.grfAccessMode = GRANT_ACCESS; - new_access.grfAccessPermissions = access; - new_access.grfInheritance = NO_INHERITANCE; - - new_access.Trustee.pMultipleTrustee = NULL; - new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID; - new_access.Trustee.ptstrName = reinterpret_cast( - const_cast(sid.GetPSID())); - - if (ERROR_SUCCESS != ::SetEntriesInAcl(1, &new_access, old_dacl, new_dacl)) - return false; - - return true; -} - -bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access) { - if (token == NULL) - return false; - - scoped_ptr_malloc default_dacl; - if (!GetDefaultDacl(token, &default_dacl)) - return false; - - ACL* new_dacl = NULL; - if (!AddSidToDacl(sid, default_dacl->DefaultDacl, access, &new_dacl)) - return false; - - TOKEN_DEFAULT_DACL new_token_dacl = {0}; - new_token_dacl.DefaultDacl = new_dacl; - - BOOL ret = ::SetTokenInformation(token, TokenDefaultDacl, &new_token_dacl, - sizeof(new_token_dacl)); - ::LocalFree(new_dacl); - return (TRUE == ret); -} - -bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access) { - DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; - TOKEN_USER* token_user = reinterpret_cast(malloc(size)); - - scoped_ptr_malloc token_user_ptr(token_user); - - if (!::GetTokenInformation(token, TokenUser, token_user, size, &size)) - return false; - - return AddSidToDefaultDacl(token, - reinterpret_cast(token_user->User.Sid), - access); -} - -bool AddKnownSidToKernelObject(HANDLE object, const Sid& sid, - ACCESS_MASK access) { - PSECURITY_DESCRIPTOR descriptor = NULL; - PACL old_dacl = NULL; - PACL new_dacl = NULL; - - if (ERROR_SUCCESS != ::GetSecurityInfo(object, SE_KERNEL_OBJECT, - DACL_SECURITY_INFORMATION, NULL, NULL, - &old_dacl, NULL, &descriptor)) - return false; - - if (!AddSidToDacl(sid.GetPSID(), old_dacl, access, &new_dacl)) { - ::LocalFree(descriptor); - return false; - } - - DWORD result = ::SetSecurityInfo(object, SE_KERNEL_OBJECT, - DACL_SECURITY_INFORMATION, NULL, NULL, - new_dacl, NULL); - - ::LocalFree(new_dacl); - ::LocalFree(descriptor); - - if (ERROR_SUCCESS != result) - return false; - - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/acl.h b/sandbox/src/acl.h deleted file mode 100644 index d452011..0000000 --- a/sandbox/src/acl.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_ACL_H_ -#define SANDBOX_SRC_ACL_H_ - -#include - -#include "base/memory/scoped_ptr.h" -#include "sandbox/src/sid.h" - -namespace sandbox { - -// Returns the default dacl from the token passed in. -bool GetDefaultDacl(HANDLE token, - scoped_ptr_malloc* default_dacl); - -// Appends an ACE represented by |sid| and |access| to |old_dacl|. If the -// function succeeds, new_dacl contains the new dacl and must be freed using -// LocalFree. -bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MASK access, - ACL** new_dacl); - -// Adds and ACE represented by |sid| and |access| to the default dacl present -// in the token. -bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access); - -// Adds an ACE represented by the user sid and |access| to the default dacl -// present in the token. -bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access); - -// Adds an ACE represented by |known_sid| and |access| to the dacl of the kernel -// object referenced by |object|. -bool AddKnownSidToKernelObject(HANDLE object, const Sid& sid, - ACCESS_MASK access); - -} // namespace sandbox - - -#endif // SANDBOX_SRC_ACL_H_ diff --git a/sandbox/src/broker_services.cc b/sandbox/src/broker_services.cc deleted file mode 100644 index 7f46abe..0000000 --- a/sandbox/src/broker_services.cc +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/broker_services.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/threading/platform_thread.h" -#include "base/win/scoped_handle.h" -#include "base/win/scoped_process_information.h" -#include "sandbox/src/sandbox_policy_base.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/target_process.h" -#include "sandbox/src/win2k_threadpool.h" -#include "sandbox/src/win_utils.h" - -namespace { - -// Utility function to associate a completion port to a job object. -bool AssociateCompletionPort(HANDLE job, HANDLE port, void* key) { - JOBOBJECT_ASSOCIATE_COMPLETION_PORT job_acp = { key, port }; - return ::SetInformationJobObject(job, - JobObjectAssociateCompletionPortInformation, - &job_acp, sizeof(job_acp))? true : false; -} - -// Utility function to do the cleanup necessary when something goes wrong -// while in SpawnTarget and we must terminate the target process. -sandbox::ResultCode SpawnCleanup(sandbox::TargetProcess* target, DWORD error) { - if (0 == error) - error = ::GetLastError(); - - target->Terminate(); - delete target; - ::SetLastError(error); - return sandbox::SBOX_ERROR_GENERIC; -} - -// the different commands that you can send to the worker thread that -// executes TargetEventsThread(). -enum { - THREAD_CTRL_NONE, - THREAD_CTRL_REMOVE_PEER, - THREAD_CTRL_QUIT, - THREAD_CTRL_LAST, -}; - -// Helper structure that allows the Broker to associate a job notification -// with a job object and with a policy. -struct JobTracker { - HANDLE job; - sandbox::PolicyBase* policy; - JobTracker(HANDLE cjob, sandbox::PolicyBase* cpolicy) - : job(cjob), policy(cpolicy) { - } -}; - -// Helper structure that allows the broker to track peer processes -struct PeerTracker { - HANDLE wait_object; - base::win::ScopedHandle process; - DWORD id; - HANDLE job_port; - PeerTracker(DWORD process_id, HANDLE broker_job_port) - : wait_object(NULL), id(process_id), job_port(broker_job_port) { - } -}; - -void DeregisterPeerTracker(PeerTracker* peer) { - // Deregistration shouldn't fail, but we leak rather than crash if it does. - if (::UnregisterWaitEx(peer->wait_object, INVALID_HANDLE_VALUE)) { - delete peer; - } else { - NOTREACHED(); - } -} - -} // namespace - -namespace sandbox { - -BrokerServicesBase::BrokerServicesBase() - : thread_pool_(NULL), job_port_(NULL), no_targets_(NULL), - job_thread_(NULL) { -} - -// The broker uses a dedicated worker thread that services the job completion -// port to perform policy notifications and associated cleanup tasks. -ResultCode BrokerServicesBase::Init() { - if ((NULL != job_port_) || (NULL != thread_pool_)) - return SBOX_ERROR_UNEXPECTED_CALL; - - ::InitializeCriticalSection(&lock_); - - job_port_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); - if (NULL == job_port_) - return SBOX_ERROR_GENERIC; - - no_targets_ = ::CreateEventW(NULL, TRUE, FALSE, NULL); - - job_thread_ = ::CreateThread(NULL, 0, // Default security and stack. - TargetEventsThread, this, NULL, NULL); - if (NULL == job_thread_) - return SBOX_ERROR_GENERIC; - - return SBOX_ALL_OK; -} - -// The destructor should only be called when the Broker process is terminating. -// Since BrokerServicesBase is a singleton, this is called from the CRT -// termination handlers, if this code lives on a DLL it is called during -// DLL_PROCESS_DETACH in other words, holding the loader lock, so we cannot -// wait for threads here. -BrokerServicesBase::~BrokerServicesBase() { - // If there is no port Init() was never called successfully. - if (!job_port_) - return; - - // Closing the port causes, that no more Job notifications are delivered to - // the worker thread and also causes the thread to exit. This is what we - // want to do since we are going to close all outstanding Jobs and notifying - // the policy objects ourselves. - ::PostQueuedCompletionStatus(job_port_, 0, THREAD_CTRL_QUIT, FALSE); - ::CloseHandle(job_port_); - - if (WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_, 1000)) { - // Cannot clean broker services. - NOTREACHED(); - return; - } - - JobTrackerList::iterator it; - for (it = tracker_list_.begin(); it != tracker_list_.end(); ++it) { - JobTracker* tracker = (*it); - FreeResources(tracker); - delete tracker; - } - ::CloseHandle(job_thread_); - delete thread_pool_; - ::CloseHandle(no_targets_); - - // Cancel the wait events and delete remaining peer trackers. - for (PeerTrackerMap::iterator it = peer_map_.begin(); - it != peer_map_.end(); ++it) { - DeregisterPeerTracker(it->second); - } - - // If job_port_ isn't NULL, assumes that the lock has been initialized. - if (job_port_) - ::DeleteCriticalSection(&lock_); -} - -TargetPolicy* BrokerServicesBase::CreatePolicy() { - // If you change the type of the object being created here you must also - // change the downcast to it in SpawnTarget(). - return new PolicyBase; -} - -void BrokerServicesBase::FreeResources(JobTracker* tracker) { - if (NULL != tracker->policy) { - BOOL res = ::TerminateJobObject(tracker->job, SBOX_ALL_OK); - DCHECK(res); - // Closing the job causes the target process to be destroyed so this - // needs to happen before calling OnJobEmpty(). - res = ::CloseHandle(tracker->job); - DCHECK(res); - // In OnJobEmpty() we don't actually use the job handle directly. - tracker->policy->OnJobEmpty(tracker->job); - tracker->policy->Release(); - tracker->policy = NULL; - } -} - -// The worker thread stays in a loop waiting for asynchronous notifications -// from the job objects. Right now we only care about knowing when the last -// process on a job terminates, but in general this is the place to tell -// the policy about events. -DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { - if (NULL == param) - return 1; - - base::PlatformThread::SetName("BrokerEvent"); - - BrokerServicesBase* broker = reinterpret_cast(param); - HANDLE port = broker->job_port_; - HANDLE no_targets = broker->no_targets_; - - int target_counter = 0; - ::ResetEvent(no_targets); - - while (true) { - DWORD events = 0; - ULONG_PTR key = 0; - LPOVERLAPPED ovl = NULL; - - if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE)) - // this call fails if the port has been closed before we have a - // chance to service the last packet which is 'exit' anyway so - // this is not an error. - return 1; - - if (key > THREAD_CTRL_LAST) { - // The notification comes from a job object. There are nine notifications - // that jobs can send and some of them depend on the job attributes set. - JobTracker* tracker = reinterpret_cast(key); - - switch (events) { - case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: { - // The job object has signaled that the last process associated - // with it has terminated. Assuming there is no way for a process - // to appear out of thin air in this job, it safe to assume that - // we can tell the policy to destroy the target object, and for - // us to release our reference to the policy object. - FreeResources(tracker); - break; - } - - case JOB_OBJECT_MSG_NEW_PROCESS: { - ++target_counter; - if (1 == target_counter) { - ::ResetEvent(no_targets); - } - break; - } - - case JOB_OBJECT_MSG_EXIT_PROCESS: - case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: { - { - AutoLock lock(&broker->lock_); - broker->child_process_ids_.erase(reinterpret_cast(ovl)); - } - --target_counter; - if (0 == target_counter) - ::SetEvent(no_targets); - - DCHECK(target_counter >= 0); - break; - } - - case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: { - break; - } - - default: { - NOTREACHED(); - break; - } - } - } else if (THREAD_CTRL_REMOVE_PEER == key) { - // Remove a process from our list of peers. - AutoLock lock(&broker->lock_); - PeerTrackerMap::iterator it = - broker->peer_map_.find(reinterpret_cast(ovl)); - DeregisterPeerTracker(it->second); - broker->peer_map_.erase(it); - } else if (THREAD_CTRL_QUIT == key) { - // The broker object is being destroyed so the thread needs to exit. - return 0; - } else { - // We have not implemented more commands. - NOTREACHED(); - } - } - - NOTREACHED(); - return 0; -} - -// SpawnTarget does all the interesting sandbox setup and creates the target -// process inside the sandbox. -ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, - const wchar_t* command_line, - TargetPolicy* policy, - PROCESS_INFORMATION* target_info) { - if (!exe_path) - return SBOX_ERROR_BAD_PARAMS; - - if (!policy) - return SBOX_ERROR_BAD_PARAMS; - - // Even though the resources touched by SpawnTarget can be accessed in - // multiple threads, the method itself cannot be called from more than - // 1 thread. This is to protect the global variables used while setting up - // the child process. - static DWORD thread_id = ::GetCurrentThreadId(); - DCHECK(thread_id == ::GetCurrentThreadId()); - - AutoLock lock(&lock_); - - // This downcast is safe as long as we control CreatePolicy() - PolicyBase* policy_base = static_cast(policy); - - // Construct the tokens and the job object that we are going to associate - // with the soon to be created target process. - HANDLE initial_token_temp; - HANDLE lockdown_token_temp; - DWORD win_result = policy_base->MakeTokens(&initial_token_temp, - &lockdown_token_temp); - base::win::ScopedHandle initial_token(initial_token_temp); - base::win::ScopedHandle lockdown_token(lockdown_token_temp); - - if (ERROR_SUCCESS != win_result) - return SBOX_ERROR_GENERIC; - - HANDLE job_temp; - win_result = policy_base->MakeJobObject(&job_temp); - base::win::ScopedHandle job(job_temp); - if (ERROR_SUCCESS != win_result) - return SBOX_ERROR_GENERIC; - - if (ERROR_ALREADY_EXISTS == ::GetLastError()) - return SBOX_ERROR_GENERIC; - - // Construct the thread pool here in case it is expensive. - // The thread pool is shared by all the targets - if (NULL == thread_pool_) - thread_pool_ = new Win2kThreadPool(); - - // Create the TargetProces object and spawn the target suspended. Note that - // Brokerservices does not own the target object. It is owned by the Policy. - base::win::ScopedProcessInformation process_info; - TargetProcess* target = new TargetProcess(initial_token.Take(), - lockdown_token.Take(), - job, - thread_pool_); - - std::wstring desktop = policy_base->GetAlternateDesktop(); - - win_result = target->Create(exe_path, command_line, - desktop.empty() ? NULL : desktop.c_str(), - &process_info); - if (ERROR_SUCCESS != win_result) - return SpawnCleanup(target, win_result); - - // Now the policy is the owner of the target. - if (!policy_base->AddTarget(target)) { - return SpawnCleanup(target, 0); - } - - // We are going to keep a pointer to the policy because we'll call it when - // the job object generates notifications using the completion port. - policy_base->AddRef(); - scoped_ptr tracker(new JobTracker(job.Take(), policy_base)); - if (!AssociateCompletionPort(tracker->job, job_port_, tracker.get())) - return SpawnCleanup(target, 0); - // Save the tracker because in cleanup we might need to force closing - // the Jobs. - tracker_list_.push_back(tracker.release()); - child_process_ids_.insert(process_info.process_id()); - - *target_info = process_info.Take(); - return SBOX_ALL_OK; -} - - -ResultCode BrokerServicesBase::WaitForAllTargets() { - ::WaitForSingleObject(no_targets_, INFINITE); - return SBOX_ALL_OK; -} - -bool BrokerServicesBase::IsActiveTarget(DWORD process_id) { - AutoLock lock(&lock_); - return child_process_ids_.find(process_id) != child_process_ids_.end() || - peer_map_.find(process_id) != peer_map_.end(); -} - -VOID CALLBACK BrokerServicesBase::RemovePeer(PVOID parameter, BOOLEAN timeout) { - PeerTracker* peer = reinterpret_cast(parameter); - // Don't check the return code because we this may fail (safely) at shutdown. - ::PostQueuedCompletionStatus(peer->job_port, 0, THREAD_CTRL_REMOVE_PEER, - reinterpret_cast(peer->id)); -} - -ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) { - scoped_ptr peer(new PeerTracker(::GetProcessId(peer_process), - job_port_)); - if (!peer->id) - return SBOX_ERROR_GENERIC; - - HANDLE process_handle; - if (!::DuplicateHandle(::GetCurrentProcess(), peer_process, - ::GetCurrentProcess(), &process_handle, - SYNCHRONIZE, FALSE, 0)) { - return SBOX_ERROR_GENERIC; - } - peer->process.Set(process_handle); - - AutoLock lock(&lock_); - if (!peer_map_.insert(std::make_pair(peer->id, peer.get())).second) - return SBOX_ERROR_BAD_PARAMS; - - if (!::RegisterWaitForSingleObject( - &peer->wait_object, peer->process, RemovePeer, peer.get(), INFINITE, - WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD)) { - peer_map_.erase(peer->id); - return SBOX_ERROR_GENERIC; - } - - // Release the pointer since it will be cleaned up by the callback. - peer.release(); - return SBOX_ALL_OK; -} - -} // namespace sandbox diff --git a/sandbox/src/broker_services.h b/sandbox/src/broker_services.h deleted file mode 100644 index 1d9c730..0000000 --- a/sandbox/src/broker_services.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_BROKER_SERVICES_H__ -#define SANDBOX_SRC_BROKER_SERVICES_H__ - -#include -#include -#include -#include "base/basictypes.h" -#include "base/win/scoped_handle.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/job.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sharedmem_ipc_server.h" -#include "sandbox/src/win2k_threadpool.h" -#include "sandbox/src/win_utils.h" - -namespace { - -struct JobTracker; -struct PeerTracker; - -} // namespace - -namespace sandbox { - -class PolicyBase; - -// BrokerServicesBase --------------------------------------------------------- -// Broker implementation version 0 -// -// This is an implementation of the interface BrokerServices and -// of the associated TargetProcess interface. In this implementation -// TargetProcess is a friend of BrokerServices where the later manages a -// collection of the former. -class BrokerServicesBase : public BrokerServices, - public SingletonBase { - public: - BrokerServicesBase(); - - ~BrokerServicesBase(); - - // The next five methods are the BrokerServices interface - virtual ResultCode Init(); - - virtual TargetPolicy* CreatePolicy(); - - virtual ResultCode SpawnTarget(const wchar_t* exe_path, - const wchar_t* command_line, - TargetPolicy* policy, - PROCESS_INFORMATION* target); - - virtual ResultCode WaitForAllTargets(); - - virtual ResultCode AddTargetPeer(HANDLE peer_process); - - // Checks if the supplied process ID matches one of the broker's active - // target processes - // Returns: - // true if there is an active target process for this ID, otherwise false. - bool IsActiveTarget(DWORD process_id); - - private: - // Releases the Job and notifies the associated Policy object to its - // resources as well. - static void FreeResources(JobTracker* tracker); - - // The routine that the worker thread executes. It is in charge of - // notifications and cleanup-related tasks. - static DWORD WINAPI TargetEventsThread(PVOID param); - - // Removes a target peer from the process list if it expires. - static VOID CALLBACK RemovePeer(PVOID parameter, BOOLEAN timeout); - - // The completion port used by the job objects to communicate events to - // the worker thread. - HANDLE job_port_; - - // Handle to a manual-reset event that is signaled when the total target - // process count reaches zero. - HANDLE no_targets_; - - // Handle to the worker thread that reacts to job notifications. - HANDLE job_thread_; - - // Lock used to protect the list of targets from being modified by 2 - // threads at the same time. - CRITICAL_SECTION lock_; - - // provides a pool of threads that are used to wait on the IPC calls. - ThreadProvider* thread_pool_; - - // List of the trackers for closing and cleanup purposes. - typedef std::list JobTrackerList; - JobTrackerList tracker_list_; - - // Maps peer process IDs to the saved handle and wait event. - // Prevents peer callbacks from accessing the broker after destruction. - typedef std::map PeerTrackerMap; - PeerTrackerMap peer_map_; - - // Provides a fast lookup to identify sandboxed processes. - std::set child_process_ids_; - - DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase); -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_BROKER_SERVICES_H__ diff --git a/sandbox/src/crosscall_client.h b/sandbox/src/crosscall_client.h deleted file mode 100644 index e92c1d1..0000000 --- a/sandbox/src/crosscall_client.h +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_CROSSCALL_CLIENT_H_ -#define SANDBOX_SRC_CROSSCALL_CLIENT_H_ - -#include "sandbox/src/crosscall_params.h" -#include "sandbox/src/sandbox.h" - -// This header defines the CrossCall(..) family of templated functions -// Their purpose is to simulate the syntax of regular call but to generate -// and IPC from the client-side. -// -// The basic pattern is to -// 1) use template argument deduction to compute the size of each -// parameter and the appropriate copy method -// 2) pack the parameters in the appropriate ActualCallParams< > object -// 3) call the IPC interface IPCProvider::DoCall( ) -// -// The general interface of CrossCall is: -// ResultCode CrossCall(IPCProvider& ipc_provider, -// uint32 tag, -// const Par1& p1, const Par2& p2,...pn -// CrossCallReturn* answer) -// -// where: -// ipc_provider: is a specific implementation of the ipc transport see -// sharedmem_ipc_server.h for an example. -// tag : is the unique id for this IPC call. Is used to route the call to -// the appropriate service. -// p1, p2,.. pn : The input parameters of the IPC. Use only simple types -// and wide strings (can add support for others). -// answer : If the IPC was successful. The server-side answer is here. The -// interpretation of the answer is private to client and server. -// -// The return value is ALL_OK if the IPC was delivered to the server, other -// return codes indicate that the IPC transport failed to deliver it. -namespace sandbox { - -// this is the assumed channel size. This can be overridden in a given -// IPC implementation. -const uint32 kIPCChannelSize = 1024; - -// The copy helper uses templates to deduce the appropriate copy function to -// copy the input parameters in the buffer that is going to be send across the -// IPC. These template facility can be made more sophisticated as need arises. - -// The default copy helper. It catches the general case where no other -// specialized template matches better. We set the type to ULONG_TYPE, so this -// only works with objects whose size is 32 bits. -template -class CopyHelper { - public: - CopyHelper(const T& t) : t_(t) {} - - // Returns the pointer to the start of the input. - const void* GetStart() const { - return &t_; - } - - // Update the stored value with the value in the buffer. This is not - // supported for this type. - bool Update(void* buffer) { - // Not supported; - return true; - } - - // Returns the size of the input in bytes. - uint32 GetSize() const { - return sizeof(T); - } - - // Returns true if the current type is used as an In or InOut parameter. - bool IsInOut() { - return false; - } - - // Returns this object's type. - ArgType GetType() { - COMPILE_ASSERT(sizeof(T) == sizeof(uint32), need_specialization); - return ULONG_TYPE; - } - - private: - const T& t_; -}; - -// This copy helper template specialization if for the void pointer -// case both 32 and 64 bit. -template<> -class CopyHelper { - public: - CopyHelper(void* t) : t_(t) {} - - // Returns the pointer to the start of the input. - const void* GetStart() const { - return &t_; - } - - // Update the stored value with the value in the buffer. This is not - // supported for this type. - bool Update(void* buffer) { - // Not supported; - return true; - } - - // Returns the size of the input in bytes. - uint32 GetSize() const { - return sizeof(t_); - } - - // Returns true if the current type is used as an In or InOut parameter. - bool IsInOut() { - return false; - } - - // Returns this object's type. - ArgType GetType() { - return VOIDPTR_TYPE; - } - - private: - const void* t_; -}; - -// This copy helper template specialization catches the cases where the -// parameter is a pointer to a string. -template<> -class CopyHelper { - public: - CopyHelper(const wchar_t* t) - : t_(t) { - } - - // Returns the pointer to the start of the string. - const void* GetStart() const { - return t_; - } - - // Update the stored value with the value in the buffer. This is not - // supported for this type. - bool Update(void* buffer) { - // Not supported; - return true; - } - - // Returns the size of the string in bytes. We define a NULL string to - // be of zero length. - uint32 GetSize() const { - __try { - return (!t_) ? 0 : static_cast(StringLength(t_) * sizeof(t_[0])); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - return kuint32max; - } - } - - // Returns true if the current type is used as an In or InOut parameter. - bool IsInOut() { - return false; - } - - ArgType GetType() { - return WCHAR_TYPE; - } - - private: - // We provide our not very optimized version of wcslen(), since we don't - // want to risk having the linker use the version in the CRT since the CRT - // might not be present when we do an early IPC call. - static size_t __cdecl StringLength(const wchar_t* wcs) { - const wchar_t *eos = wcs; - while (*eos++); - return static_cast(eos - wcs - 1); - } - - const wchar_t* t_; -}; - -// Specialization for non-const strings. We just reuse the implementation of the -// const string specialization. -template<> -class CopyHelper : public CopyHelper { - public: - typedef CopyHelper Base; - CopyHelper(wchar_t* t) : Base(t) {} - - const void* GetStart() const { - return Base::GetStart(); - } - - bool Update(void* buffer) { - return Base::Update(buffer); - } - - uint32 GetSize() const { - return Base::GetSize(); - } - - bool IsInOut() { - return Base::IsInOut(); - } - - ArgType GetType() { - return Base::GetType(); - } -}; - -// Specialization for wchar_t arrays strings. We just reuse the implementation -// of the const string specialization. -template -class CopyHelper : public CopyHelper { - public: - typedef const wchar_t array[n]; - typedef CopyHelper Base; - CopyHelper(array t) : Base(t) {} - - const void* GetStart() const { - return Base::GetStart(); - } - - bool Update(void* buffer) { - return Base::Update(buffer); - } - - uint32 GetSize() const { - return Base::GetSize(); - } - - bool IsInOut() { - return Base::IsInOut(); - } - - ArgType GetType() { - return Base::GetType(); - } -}; - -// Generic encapsulation class containing a pointer to a buffer and the -// size of the buffer. It is used by the IPC to be able to pass in/out -// parameters. -class InOutCountedBuffer : public CountedBuffer { - public: - InOutCountedBuffer(void* buffer, uint32 size) : CountedBuffer(buffer, size) {} -}; - -// This copy helper template specialization catches the cases where the -// parameter is a an input/output buffer. -template<> -class CopyHelper { - public: - CopyHelper(const InOutCountedBuffer t) : t_(t) {} - - // Returns the pointer to the start of the string. - const void* GetStart() const { - return t_.Buffer(); - } - - // Updates the buffer with the value from the new buffer in parameter. - bool Update(void* buffer) { - // We are touching user memory, this has to be done from inside a try - // except. - __try { - memcpy(t_.Buffer(), buffer, t_.Size()); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - return false; - } - return true; - } - - // Returns the size of the string in bytes. We define a NULL string to - // be of zero length. - uint32 GetSize() const { - return t_.Size(); - } - - // Returns true if the current type is used as an In or InOut parameter. - bool IsInOut() { - return true; - } - - ArgType GetType() { - return INOUTPTR_TYPE; - } - - private: - const InOutCountedBuffer t_; -}; - -// The following two macros make it less error prone the generation -// of CrossCall functions with ever more input parameters. - -#define XCALL_GEN_PARAMS_OBJ(num, params) \ - typedef ActualCallParams ActualParams; \ - void* raw_mem = ipc_provider.GetBuffer(); \ - if (NULL == raw_mem) \ - return SBOX_ERROR_NO_SPACE; \ - ActualParams* params = new(raw_mem) ActualParams(tag); - -#define XCALL_GEN_COPY_PARAM(num, params) \ - COMPILE_ASSERT(kMaxIpcParams >= num, too_many_parameters); \ - CopyHelper ch##num(p##num); \ - if (!params->CopyParamIn(num - 1, ch##num.GetStart(), ch##num.GetSize(), \ - ch##num.IsInOut(), ch##num.GetType())) \ - return SBOX_ERROR_NO_SPACE; - -#define XCALL_GEN_UPDATE_PARAM(num, params) \ - if (!ch##num.Update(params->GetParamPtr(num-1))) {\ - ipc_provider.FreeBuffer(raw_mem); \ - return SBOX_ERROR_BAD_PARAMS; \ - } - -#define XCALL_GEN_FREE_CHANNEL() \ - ipc_provider.FreeBuffer(raw_mem); - -// CrossCall template with one input parameter -template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - CrossCallReturn* answer) { - XCALL_GEN_PARAMS_OBJ(1, call_params); - XCALL_GEN_COPY_PARAM(1, call_params); - - ResultCode result = ipc_provider.DoCall(call_params, answer); - - if (SBOX_ERROR_CHANNEL_ERROR != result) { - XCALL_GEN_UPDATE_PARAM(1, call_params); - XCALL_GEN_FREE_CHANNEL(); - } - - return result; -} - -// CrossCall template with two input parameters. -template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, CrossCallReturn* answer) { - XCALL_GEN_PARAMS_OBJ(2, call_params); - XCALL_GEN_COPY_PARAM(1, call_params); - XCALL_GEN_COPY_PARAM(2, call_params); - - ResultCode result = ipc_provider.DoCall(call_params, answer); - - if (SBOX_ERROR_CHANNEL_ERROR != result) { - XCALL_GEN_UPDATE_PARAM(1, call_params); - XCALL_GEN_UPDATE_PARAM(2, call_params); - XCALL_GEN_FREE_CHANNEL(); - } - return result; -} - -// CrossCall template with three input parameters. -template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, const Par3& p3, CrossCallReturn* answer) { - XCALL_GEN_PARAMS_OBJ(3, call_params); - XCALL_GEN_COPY_PARAM(1, call_params); - XCALL_GEN_COPY_PARAM(2, call_params); - XCALL_GEN_COPY_PARAM(3, call_params); - - ResultCode result = ipc_provider.DoCall(call_params, answer); - - if (SBOX_ERROR_CHANNEL_ERROR != result) { - XCALL_GEN_UPDATE_PARAM(1, call_params); - XCALL_GEN_UPDATE_PARAM(2, call_params); - XCALL_GEN_UPDATE_PARAM(3, call_params); - XCALL_GEN_FREE_CHANNEL(); - } - return result; -} - -// CrossCall template with four input parameters. -template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, const Par3& p3, const Par4& p4, - CrossCallReturn* answer) { - XCALL_GEN_PARAMS_OBJ(4, call_params); - XCALL_GEN_COPY_PARAM(1, call_params); - XCALL_GEN_COPY_PARAM(2, call_params); - XCALL_GEN_COPY_PARAM(3, call_params); - XCALL_GEN_COPY_PARAM(4, call_params); - - ResultCode result = ipc_provider.DoCall(call_params, answer); - - if (SBOX_ERROR_CHANNEL_ERROR != result) { - XCALL_GEN_UPDATE_PARAM(1, call_params); - XCALL_GEN_UPDATE_PARAM(2, call_params); - XCALL_GEN_UPDATE_PARAM(3, call_params); - XCALL_GEN_UPDATE_PARAM(4, call_params); - XCALL_GEN_FREE_CHANNEL(); - } - return result; -} - -// CrossCall template with five input parameters. -template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, const Par3& p3, const Par4& p4, - const Par5& p5, CrossCallReturn* answer) { - XCALL_GEN_PARAMS_OBJ(5, call_params); - XCALL_GEN_COPY_PARAM(1, call_params); - XCALL_GEN_COPY_PARAM(2, call_params); - XCALL_GEN_COPY_PARAM(3, call_params); - XCALL_GEN_COPY_PARAM(4, call_params); - XCALL_GEN_COPY_PARAM(5, call_params); - - ResultCode result = ipc_provider.DoCall(call_params, answer); - - if (SBOX_ERROR_CHANNEL_ERROR != result) { - XCALL_GEN_UPDATE_PARAM(1, call_params); - XCALL_GEN_UPDATE_PARAM(2, call_params); - XCALL_GEN_UPDATE_PARAM(3, call_params); - XCALL_GEN_UPDATE_PARAM(4, call_params); - XCALL_GEN_UPDATE_PARAM(5, call_params); - XCALL_GEN_FREE_CHANNEL(); - } - return result; -} - -// CrossCall template with six input parameters. -template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, const Par3& p3, const Par4& p4, - const Par5& p5, const Par6& p6, CrossCallReturn* answer) { - XCALL_GEN_PARAMS_OBJ(6, call_params); - XCALL_GEN_COPY_PARAM(1, call_params); - XCALL_GEN_COPY_PARAM(2, call_params); - XCALL_GEN_COPY_PARAM(3, call_params); - XCALL_GEN_COPY_PARAM(4, call_params); - XCALL_GEN_COPY_PARAM(5, call_params); - XCALL_GEN_COPY_PARAM(6, call_params); - - ResultCode result = ipc_provider.DoCall(call_params, answer); - - if (SBOX_ERROR_CHANNEL_ERROR != result) { - XCALL_GEN_UPDATE_PARAM(1, call_params); - XCALL_GEN_UPDATE_PARAM(2, call_params); - XCALL_GEN_UPDATE_PARAM(3, call_params); - XCALL_GEN_UPDATE_PARAM(4, call_params); - XCALL_GEN_UPDATE_PARAM(5, call_params); - XCALL_GEN_UPDATE_PARAM(6, call_params); - XCALL_GEN_FREE_CHANNEL(); - } - return result; -} - -// CrossCall template with seven input parameters. -template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, const Par3& p3, const Par4& p4, - const Par5& p5, const Par6& p6, const Par7& p7, - CrossCallReturn* answer) { - XCALL_GEN_PARAMS_OBJ(7, call_params); - XCALL_GEN_COPY_PARAM(1, call_params); - XCALL_GEN_COPY_PARAM(2, call_params); - XCALL_GEN_COPY_PARAM(3, call_params); - XCALL_GEN_COPY_PARAM(4, call_params); - XCALL_GEN_COPY_PARAM(5, call_params); - XCALL_GEN_COPY_PARAM(6, call_params); - XCALL_GEN_COPY_PARAM(7, call_params); - - ResultCode result = ipc_provider.DoCall(call_params, answer); - - if (SBOX_ERROR_CHANNEL_ERROR != result) { - XCALL_GEN_UPDATE_PARAM(1, call_params); - XCALL_GEN_UPDATE_PARAM(2, call_params); - XCALL_GEN_UPDATE_PARAM(3, call_params); - XCALL_GEN_UPDATE_PARAM(4, call_params); - XCALL_GEN_UPDATE_PARAM(5, call_params); - XCALL_GEN_UPDATE_PARAM(6, call_params); - XCALL_GEN_UPDATE_PARAM(7, call_params); - XCALL_GEN_FREE_CHANNEL(); - } - return result; -} -} // namespace sandbox - -#endif // SANDBOX_SRC_CROSSCALL_CLIENT_H__ diff --git a/sandbox/src/crosscall_params.h b/sandbox/src/crosscall_params.h deleted file mode 100644 index e4c047b..0000000 --- a/sandbox/src/crosscall_params.h +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_CROSSCALL_PARAMS_H__ -#define SANDBOX_SRC_CROSSCALL_PARAMS_H__ - -#include -#include - -#include - -#include "base/basictypes.h" -#include "sandbox/src/internal_types.h" -#include "sandbox/src/sandbox_types.h" - -namespace { - -// Increases |value| until there is no need for padding given an int64 -// alignment. Returns the increased value. -uint32 Align(uint32 value) { - uint32 alignment = sizeof(int64); - return ((value + alignment - 1) / alignment) * alignment; -} - -} -// This header is part of CrossCall: the sandbox inter-process communication. -// This header defines the basic types used both in the client IPC and in the -// server IPC code. CrossCallParams and ActualCallParams model the input -// parameters of an IPC call and CrossCallReturn models the output params and -// the return value. -// -// An IPC call is defined by its 'tag' which is a (uint32) unique identifier -// that is used to route the IPC call to the proper server. Every tag implies -// a complete call signature including the order and type of each parameter. -// -// Like most IPC systems. CrossCall is designed to take as inputs 'simple' -// types such as integers and strings. Classes, generic arrays or pointers to -// them are not supported. -// -// Another limitation of CrossCall is that the return value and output -// parameters can only be uint32 integers. Returning complex structures or -// strings is not supported. - -namespace sandbox { - -// max number of extended return parameters. See CrossCallReturn -const size_t kExtendedReturnCount = 8; - -// Union of multiple types to be used as extended results -// in the CrossCallReturn. -union MultiType { - uint32 unsigned_int; - void* pointer; - HANDLE handle; - ULONG_PTR ulong_ptr; -}; - -// Maximum number of IPC parameters currently supported. -// To increase this value, we have to: -// - Add another Callback typedef to Dispatcher. -// - Add another case to the switch on SharedMemIPCServer::InvokeCallback. -// - Add another case to the switch in GetActualAndMaxBufferSize -const int kMaxIpcParams = 9; - -// Contains the information about a parameter in the ipc buffer. -struct ParamInfo { - ArgType type_; - uint32 offset_; - uint32 size_; -}; - -// Models the return value and the return parameters of an IPC call -// currently limited to one status code and eight generic return values -// which cannot be pointers to other data. For x64 ports this structure -// might have to use other integer types. -struct CrossCallReturn { - // the IPC tag. It should match the original IPC tag. - uint32 tag; - // The result of the IPC operation itself. - ResultCode call_outcome; - // the result of the IPC call as executed in the server. The interpretation - // of this value depends on the specific service. - union { - NTSTATUS nt_status; - DWORD win32_result; - }; - // Number of extended return values. - uint32 extended_count; - // for calls that should return a windows handle. It is found here. - HANDLE handle; - // The array of extended values. - MultiType extended[kExtendedReturnCount]; -}; - -// CrossCallParams base class that models the input params all packed in a -// single compact memory blob. The representation can vary but in general a -// given child of this class is meant to represent all input parameters -// necessary to make a IPC call. -// -// This class cannot have virtual members because its assumed the IPC -// parameters start from the 'this' pointer to the end, which is defined by -// one of the subclasses -// -// Objects of this class cannot be constructed directly. Only derived -// classes have the proper knowledge to construct it. -class CrossCallParams { - public: - // Returns the tag (ipc unique id) associated with this IPC. - uint32 GetTag() const { - return tag_; - } - - // Returns the beggining of the buffer where the IPC params can be stored. - // prior to an IPC call - const void* GetBuffer() const { - return this; - } - - // Returns how many parameter this IPC call should have. - const uint32 GetParamsCount() const { - return params_count_; - } - - // Returns a pointer to the CrossCallReturn structure. - CrossCallReturn* GetCallReturn() { - return &call_return; - } - - // Returns TRUE if this call contains InOut parameters. - const bool IsInOut() const { - return (1 == is_in_out_); - } - - // Tells the CrossCall object if it contains InOut parameters. - void SetIsInOut(bool value) { - if (value) - is_in_out_ = 1; - else - is_in_out_ = 0; - } - - protected: - // constructs the IPC call params. Called only from the derived classes - CrossCallParams(uint32 tag, uint32 params_count) - : tag_(tag), - params_count_(params_count), - is_in_out_(0) { - } - - private: - uint32 tag_; - uint32 is_in_out_; - CrossCallReturn call_return; - const uint32 params_count_; - DISALLOW_COPY_AND_ASSIGN(CrossCallParams); -}; - -// ActualCallParams models an specific IPC call parameters with respect to the -// storage allocation that the packed parameters should need. -// NUMBER_PARAMS: the number of parameters, valid from 1 to N -// BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take, -// typically the block size is defined by the channel size of the underlying -// ipc mechanism. -// In practice this class is used to levergage C++ capacity to properly -// calculate sizes and displacements given the possibility of the packed params -// blob to be complex. -// -// As is, this class assumes that the layout of the blob is as follows. Assume -// that NUMBER_PARAMS = 2 and a 32-bit build: -// -// [ tag 4 bytes] -// [ IsOnOut 4 bytes] -// [ call return 52 bytes] -// [ params count 4 bytes] -// [ parameter 0 type 4 bytes] -// [ parameter 0 offset 4 bytes] ---delta to ---\ -// [ parameter 0 size 4 bytes] | -// [ parameter 1 type 4 bytes] | -// [ parameter 1 offset 4 bytes] ---------------|--\ -// [ parameter 1 size 4 bytes] | | -// [ parameter 2 type 4 bytes] | | -// [ parameter 2 offset 4 bytes] ----------------------\ -// [ parameter 2 size 4 bytes] | | | -// |---------------------------| | | | -// | value 0 (x bytes) | <--------------/ | | -// | value 1 (y bytes) | <-----------------/ | -// | | | -// | end of buffer | <---------------------/ -// |---------------------------| -// -// Note that the actual number of params is NUMBER_PARAMS + 1 -// so that the size of each actual param can be computed from the difference -// between one parameter and the next down. The offset of the last param -// points to the end of the buffer and the type and size are undefined. -// -template -class ActualCallParams : public CrossCallParams { - public: - // constructor. Pass the ipc unique tag as input - explicit ActualCallParams(uint32 tag) - : CrossCallParams(tag, NUMBER_PARAMS) { - param_info_[0].offset_ = parameters_ - reinterpret_cast(this); - } - - // Testing-only constructor. Allows setting the |number_params| to a - // wrong value. - ActualCallParams(uint32 tag, uint32 number_params) - : CrossCallParams(tag, number_params) { - param_info_[0].offset_ = parameters_ - reinterpret_cast(this); - } - - // Testing-only method. Allows setting the apparent size to a wrong value. - // returns the previous size. - uint32 OverrideSize(uint32 new_size) { - uint32 previous_size = param_info_[NUMBER_PARAMS].offset_; - param_info_[NUMBER_PARAMS].offset_ = new_size; - return previous_size; - } - - // Copies each paramter into the internal buffer. For each you must supply: - // index: 0 for the first param, 1 for the next an so on - bool CopyParamIn(uint32 index, const void* parameter_address, uint32 size, - bool is_in_out, ArgType type) { - if (index >= NUMBER_PARAMS) { - return false; - } - - if (kuint32max == size) { - // Memory error while getting the size. - return false; - } - - if (size && !parameter_address) { - return false; - } - - if (param_info_[index].offset_ > sizeof(*this)) { - // It does not fit, abort copy. - return false; - } - - char* dest = reinterpret_cast(this) + param_info_[index].offset_; - - // We might be touching user memory, this has to be done from inside a try - // except. - __try { - memcpy(dest, parameter_address, size); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - return false; - } - - // Set the flag to tell the broker to update the buffer once the call is - // made. - if (is_in_out) - SetIsInOut(true); - - param_info_[index + 1].offset_ = Align(param_info_[index].offset_ + - size); - param_info_[index].size_ = size; - param_info_[index].type_ = type; - return true; - } - - // Returns a pointer to a parameter in the memory section. - void* GetParamPtr(size_t index) { - return reinterpret_cast(this) + param_info_[index].offset_; - } - - // Returns the total size of the buffer. Only valid once all the paramters - // have been copied in with CopyParamIn. - uint32 GetSize() const { - return param_info_[NUMBER_PARAMS].offset_; - } - - protected: - ActualCallParams() : CrossCallParams(0, NUMBER_PARAMS) { } - - private: - ParamInfo param_info_[NUMBER_PARAMS + 1]; - char parameters_[BLOCK_SIZE - sizeof(CrossCallParams) - - sizeof(ParamInfo) * (NUMBER_PARAMS + 1)]; - DISALLOW_COPY_AND_ASSIGN(ActualCallParams); -}; - -COMPILE_ASSERT(sizeof(ActualCallParams<1, 1024>) == 1024, bad_size_buffer); -COMPILE_ASSERT(sizeof(ActualCallParams<2, 1024>) == 1024, bad_size_buffer); -COMPILE_ASSERT(sizeof(ActualCallParams<3, 1024>) == 1024, bad_size_buffer); - -} // namespace sandbox - -#endif // SANDBOX_SRC_CROSSCALL_PARAMS_H__ diff --git a/sandbox/src/crosscall_server.cc b/sandbox/src/crosscall_server.cc deleted file mode 100644 index f40b677..0000000 --- a/sandbox/src/crosscall_server.cc +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/crosscall_params.h" -#include "sandbox/src/crosscall_client.h" -#include "base/logging.h" - -// This code performs the ipc message validation. Potential security flaws -// on the ipc are likelier to be found in this code than in the rest of -// the ipc code. - -namespace { - -// The buffer for a message must match the max channel size. -const size_t kMaxBufferSize = sandbox::kIPCChannelSize; - -} - -namespace sandbox { - -// Returns the actual size for the parameters in an IPC buffer. Returns -// zero if the |param_count| is zero or too big. -uint32 GetActualBufferSize(uint32 param_count, void* buffer_base) { - // The template types are used to calculate the maximum expected size. - typedef ActualCallParams<1, kMaxBufferSize> ActualCP1; - typedef ActualCallParams<2, kMaxBufferSize> ActualCP2; - typedef ActualCallParams<3, kMaxBufferSize> ActualCP3; - typedef ActualCallParams<4, kMaxBufferSize> ActualCP4; - typedef ActualCallParams<5, kMaxBufferSize> ActualCP5; - typedef ActualCallParams<6, kMaxBufferSize> ActualCP6; - typedef ActualCallParams<7, kMaxBufferSize> ActualCP7; - typedef ActualCallParams<8, kMaxBufferSize> ActualCP8; - typedef ActualCallParams<9, kMaxBufferSize> ActualCP9; - - // Retrieve the actual size and the maximum size of the params buffer. - switch (param_count) { - case 0: - return 0; - case 1: - return reinterpret_cast(buffer_base)->GetSize(); - case 2: - return reinterpret_cast(buffer_base)->GetSize(); - case 3: - return reinterpret_cast(buffer_base)->GetSize(); - case 4: - return reinterpret_cast(buffer_base)->GetSize(); - case 5: - return reinterpret_cast(buffer_base)->GetSize(); - case 6: - return reinterpret_cast(buffer_base)->GetSize(); - case 7: - return reinterpret_cast(buffer_base)->GetSize(); - case 8: - return reinterpret_cast(buffer_base)->GetSize(); - case 9: - return reinterpret_cast(buffer_base)->GetSize(); - default: - NOTREACHED(); - return 0; - } -} - -CrossCallParamsEx::CrossCallParamsEx() - :CrossCallParams(0, 0) { -} - -// We override the delete operator because the object's backing memory -// is hand allocated in CreateFromBuffer. We don't override the new operator -// because the constructors are private so there is no way to mismatch -// new & delete. -void CrossCallParamsEx::operator delete(void* raw_memory) throw() { - if (NULL == raw_memory) { - // C++ standard allows 'delete 0' behavior. - return; - } - delete[] reinterpret_cast(raw_memory); -} - -// This function uses a SEH try block so cannot use C++ objects that -// have destructors or else you get Compiler Error C2712. So no DCHECKs -// inside this function. -CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base, - uint32 buffer_size, - uint32* output_size) { - // IMPORTANT: Everything inside buffer_base and derived from it such - // as param_count and declared_size is untrusted. - if (NULL == buffer_base) { - return NULL; - } - if (buffer_size < sizeof(CrossCallParams)) { - return NULL; - } - if (buffer_size > kMaxBufferSize) { - return NULL; - } - - char* backing_mem = NULL; - uint32 param_count = 0; - uint32 declared_size; - uint32 min_declared_size; - CrossCallParamsEx* copied_params = NULL; - - // Touching the untrusted buffer is done under a SEH try block. This - // will catch memory access violations so we don't crash. - __try { - CrossCallParams* call_params = - reinterpret_cast(buffer_base); - - // Check against the minimum size given the number of stated params - // if too small we bail out. - param_count = call_params->GetParamsCount(); - min_declared_size = sizeof(CrossCallParams) + - ((param_count + 1) * sizeof(ParamInfo)); - - if ((buffer_size < min_declared_size) || - (sizeof(CrossCallParamsEx) > min_declared_size)) { - // Minimal computed size bigger than existing buffer or param_count - // integer overflow. - return NULL; - } - - // Retrieve the declared size which if it fails returns 0. - declared_size = GetActualBufferSize(param_count, buffer_base); - - if ((declared_size > buffer_size) || - (declared_size < min_declared_size)) { - // Declared size is bigger than buffer or smaller than computed size - // or param_count 0 or bigger than 9. - return NULL; - } - - // Now we copy the actual amount of the message. - *output_size = declared_size; - backing_mem = new char[declared_size]; - copied_params = reinterpret_cast(backing_mem); - memcpy(backing_mem, call_params, declared_size); - - // Check params count in case it got changed right before the memcpy. - if (copied_params->GetParamsCount() != param_count) { - delete [] backing_mem; - return NULL; - } - - } __except(EXCEPTION_EXECUTE_HANDLER) { - // In case of a windows exception we know it occurred while touching the - // untrusted buffer so we bail out as is. - delete [] backing_mem; - return NULL; - } - - const char* last_byte = &backing_mem[declared_size]; - const char* first_byte = &backing_mem[min_declared_size]; - - // Verify here that all and each parameters make sense. This is done in the - // local copy. - for (uint32 ix =0; ix != param_count; ++ix) { - uint32 size = 0; - ArgType type; - char* address = reinterpret_cast( - copied_params->GetRawParameter(ix, &size, &type)); - if ((NULL == address) || // No null params. - (INVALID_TYPE >= type) || (LAST_TYPE <= type) || // Unknown type. - (address < backing_mem) || // Start cannot point before buffer. - (address < first_byte) || // Start cannot point too low. - (address > last_byte) || // Start cannot point past buffer. - ((address + size) < address) || // Invalid size. - ((address + size) > last_byte)) { // End cannot point past buffer. - // Malformed. - delete[] backing_mem; - return NULL; - } - } - // The parameter buffer looks good. - return copied_params; -} - -// Accessors to the parameters in the raw buffer. -void* CrossCallParamsEx::GetRawParameter(uint32 index, uint32* size, - ArgType* type) { - if (index >= GetParamsCount()) { - return NULL; - } - // The size is always computed from the parameter minus the next - // parameter, this works because the message has an extra parameter slot - *size = param_info_[index].size_; - *type = param_info_[index].type_; - - return param_info_[index].offset_ + reinterpret_cast(this); -} - -// Covers common case for 32 bit integers. -bool CrossCallParamsEx::GetParameter32(uint32 index, uint32* param) { - uint32 size = 0; - ArgType type; - void* start = GetRawParameter(index, &size, &type); - if ((NULL == start) || (4 != size) || (ULONG_TYPE != type)) { - return false; - } - // Copy the 4 bytes. - *(reinterpret_cast(param)) = *(reinterpret_cast(start)); - return true; -} - -bool CrossCallParamsEx::GetParameterVoidPtr(uint32 index, void** param) { - uint32 size = 0; - ArgType type; - void* start = GetRawParameter(index, &size, &type); - if ((NULL == start) || (sizeof(void*) != size) || (VOIDPTR_TYPE != type)) { - return false; - } - *param = *(reinterpret_cast(start)); - return true; -} - -// Covers the common case of reading a string. Note that the string is not -// scanned for invalid characters. -bool CrossCallParamsEx::GetParameterStr(uint32 index, std::wstring* string) { - uint32 size = 0; - ArgType type; - void* start = GetRawParameter(index, &size, &type); - if (WCHAR_TYPE != type) { - return false; - } - - // Check if this is an empty string. - if (size == 0) { - *string = L""; - return true; - } - - if ((NULL == start) || ((size % sizeof(wchar_t)) != 0)) { - return false; - } - string->append(reinterpret_cast(start), size/(sizeof(wchar_t))); - return true; -} - -bool CrossCallParamsEx::GetParameterPtr(uint32 index, uint32 expected_size, - void** pointer) { - uint32 size = 0; - ArgType type; - void* start = GetRawParameter(index, &size, &type); - - if ((size != expected_size) || (INOUTPTR_TYPE != type)) { - return false; - } - - if (NULL == start) { - return false; - } - - *pointer = start; - return true; -} - -void SetCallError(ResultCode error, CrossCallReturn* call_return) { - call_return->call_outcome = error; - call_return->extended_count = 0; -} - -void SetCallSuccess(CrossCallReturn* call_return) { - call_return->call_outcome = SBOX_ALL_OK; -} - -Dispatcher* Dispatcher::OnMessageReady(IPCParams* ipc, - CallbackGeneric* callback) { - DCHECK(callback); - std::vector::iterator it = ipc_calls_.begin(); - for (; it != ipc_calls_.end(); ++it) { - if (it->params.Matches(ipc)) { - *callback = it->callback; - return this; - } - } - return NULL; -} - -} // namespace sandbox diff --git a/sandbox/src/crosscall_server.h b/sandbox/src/crosscall_server.h deleted file mode 100644 index 59445f6..0000000 --- a/sandbox/src/crosscall_server.h +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_CROSSCALL_SERVER_H_ -#define SANDBOX_SRC_CROSSCALL_SERVER_H_ - -#include -#include -#include "base/basictypes.h" -#include "base/callback.h" -#include "sandbox/src/crosscall_params.h" - -// This is the IPC server interface for CrossCall: The IPC for the Sandbox -// On the server, CrossCall needs two things: -// 1) threads: Or better said, someone to provide them, that is what the -// ThreadProvider interface is defined for. These thread(s) are -// the ones that will actually execute the IPC data retrieval. -// -// 2) a dispatcher: This interface represents the way to route and process -// an IPC call given the IPC tag. -// -// The other class included here CrossCallParamsEx is the server side version -// of the CrossCallParams class of /sandbox/crosscall_params.h The difference -// is that the sever version is paranoid about the correctness of the IPC -// message and will do all sorts of verifications. -// -// A general diagram of the interaction is as follows: -// -// ------------ -// | | -// ThreadProvider <--(1)Register--| IPC | -// | | Implemen | -// | | -tation | -// (2) | | OnMessage -// IPC fired --callback ------>| |--(3)---> Dispatcher -// | | -// ------------ -// -// The IPC implementation sits as a middleman between the handling of the -// specifics of scheduling a thread to service the IPC and the multiple -// entities that can potentially serve each particular IPC. -namespace sandbox { - -class InterceptionManager; - -// This function signature is required as the callback when an IPC call fires. -// context: a user-defined pointer that was set using ThreadProvider -// reason: 0 if the callback was fired because of a timeout. -// 1 if the callback was fired because of an event. -typedef void (__stdcall * CrossCallIPCCallback)(void* context, - unsigned char reason); - -// ThreadProvider models a thread factory. The idea is to decouple thread -// creation and lifetime from the inner guts of the IPC. The contract is -// simple: -// - the IPC implementation calls RegisterWait with a waitable object that -// becomes signaled when an IPC arrives and needs to be serviced. -// - when the waitable object becomes signaled, the thread provider conjures -// a thread that calls the callback (CrossCallIPCCallback) function -// - the callback function tries its best not to block and return quickly -// and should not assume that the next callback will use the same thread -// - when the callback returns the ThreadProvider owns again the thread -// and can destroy it or keep it around. -class ThreadProvider { - public: - // Registers a waitable object with the thread provider. - // client: A number to associate with all the RegisterWait calls, typically - // this is the address of the caller object. This parameter cannot - // be zero. - // waitable_object : a kernel object that can be waited on - // callback: a function pointer which is the function that will be called - // when the waitable object fires - // context: a user-provider pointer that is passed back to the callback - // when its called - virtual bool RegisterWait(const void* client, HANDLE waitable_object, - CrossCallIPCCallback callback, - void* context) = 0; - - // Removes all the registrations done with the same cookie parameter. - // This frees internal thread pool resources. - virtual bool UnRegisterWaits(void* cookie) = 0; - virtual ~ThreadProvider() {} -}; - -// Models the server-side of the original input parameters. -// Provides IPC buffer validation and it is capable of reading the parameters -// out of the IPC buffer. -class CrossCallParamsEx : public CrossCallParams { - public: - // Factory constructor. Pass an IPCbuffer (and buffer size) that contains a - // pending IPCcall. This constructor will: - // 1) validate the IPC buffer. returns NULL is the IPCbuffer is malformed. - // 2) make a copy of the IPCbuffer (parameter capture) - static CrossCallParamsEx* CreateFromBuffer(void* buffer_base, - uint32 buffer_size, - uint32* output_size); - - // Provides IPCinput parameter raw access: - // index : the parameter to read; 0 is the first parameter - // returns NULL if the parameter is non-existent. If it exists it also - // returns the size in *size - void* GetRawParameter(uint32 index, uint32* size, ArgType* type); - - // Gets a parameter that is four bytes in size. - // Returns false if the parameter does not exist or is not 32 bits wide. - bool GetParameter32(uint32 index, uint32* param); - - // Gets a parameter that is void pointer in size. - // Returns false if the parameter does not exist or is not void pointer sized. - bool GetParameterVoidPtr(uint32 index, void** param); - - // Gets a parameter that is a string. Returns false if the parameter does not - // exist. - bool GetParameterStr(uint32 index, std::wstring* string); - - // Gets a parameter that is an in/out buffer. Returns false is the parameter - // does not exist or if the size of the actual parameter is not equal to the - // expected size. - bool GetParameterPtr(uint32 index, uint32 expected_size, void** pointer); - - // Frees the memory associated with the IPC parameters. - static void operator delete(void* raw_memory) throw(); - - private: - // Only the factory method CreateFromBuffer can construct these objects. - CrossCallParamsEx(); - - ParamInfo param_info_[1]; - DISALLOW_COPY_AND_ASSIGN(CrossCallParamsEx); -}; - -// Simple helper function that sets the members of CrossCallReturn -// to the proper state to signal a basic error. -void SetCallError(ResultCode error, CrossCallReturn* call_return); - -// Sets the internal status of call_return to signify the that IPC call -// completed successfully. -void SetCallSuccess(CrossCallReturn* call_return); - -// Represents the client process that initiated the IPC which boils down to the -// process handle and the job object handle that contains the client process. -struct ClientInfo { - HANDLE process; - HANDLE job_object; - DWORD process_id; -}; - -// All IPC-related information to be passed to the IPC handler. -struct IPCInfo { - int ipc_tag; - const ClientInfo* client_info; - CrossCallReturn return_info; -}; - -// This structure identifies IPC signatures. -struct IPCParams { - int ipc_tag; - ArgType args[kMaxIpcParams]; - - bool Matches(IPCParams* other) const { - return !memcmp(this, other, sizeof(*other)); - } -}; - -// Models an entity that can process an IPC message or it can route to another -// one that could handle it. When an IPC arrives the IPC implementation will: -// 1) call OnMessageReady() with the tag of the pending IPC. If the dispatcher -// returns NULL it means that it cannot handle this IPC but if it returns -// non-null, it must be the pointer to a dispatcher that can handle it. -// 2) When the IPC finally obtains a valid Dispatcher the IPC -// implementation creates a CrossCallParamsEx from the raw IPC buffer. -// 3) It calls the returned callback, with the IPC info and arguments. -class Dispatcher { - public: - // Called from the IPC implementation to handle a specific IPC message. - typedef bool (Dispatcher::*CallbackGeneric)(); - typedef bool (Dispatcher::*Callback0)(IPCInfo* ipc); - typedef bool (Dispatcher::*Callback1)(IPCInfo* ipc, void* p1); - typedef bool (Dispatcher::*Callback2)(IPCInfo* ipc, void* p1, void* p2); - typedef bool (Dispatcher::*Callback3)(IPCInfo* ipc, void* p1, void* p2, - void* p3); - typedef bool (Dispatcher::*Callback4)(IPCInfo* ipc, void* p1, void* p2, - void* p3, void* p4); - typedef bool (Dispatcher::*Callback5)(IPCInfo* ipc, void* p1, void* p2, - void* p3, void* p4, void* p5); - typedef bool (Dispatcher::*Callback6)(IPCInfo* ipc, void* p1, void* p2, - void* p3, void* p4, void* p5, void* p6); - typedef bool (Dispatcher::*Callback7)(IPCInfo* ipc, void* p1, void* p2, - void* p3, void* p4, void* p5, void* p6, - void* p7); - typedef bool (Dispatcher::*Callback8)(IPCInfo* ipc, void* p1, void* p2, - void* p3, void* p4, void* p5, void* p6, - void* p7, void* p8); - typedef bool (Dispatcher::*Callback9)(IPCInfo* ipc, void* p1, void* p2, - void* p3, void* p4, void* p5, void* p6, - void* p7, void* p8, void* p9); - - // Called from the IPC implementation when an IPC message is ready override - // on a derived class to handle a set of IPC messages. Return NULL if your - // subclass does not handle the message or return the pointer to the subclass - // that can handle it. - virtual Dispatcher* OnMessageReady(IPCParams* ipc, CallbackGeneric* callback); - - // Called when a target proces is created, to setup the interceptions related - // with the given service (IPC). - virtual bool SetupService(InterceptionManager* manager, int service) = 0; - - virtual ~Dispatcher() {} - - protected: - // Structure that defines an IPC Call with all the parameters and the handler. - struct IPCCall { - IPCParams params; - CallbackGeneric callback; - }; - - // List of IPC Calls supported by the class. - std::vector ipc_calls_; -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_CROSSCALL_SERVER_H_ diff --git a/sandbox/src/dep.cc b/sandbox/src/dep.cc deleted file mode 100644 index 4995601..0000000 --- a/sandbox/src/dep.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/dep.h" - -#include - -#include "base/logging.h" - -namespace sandbox { - -namespace { - -// These values are in the Windows 2008 SDK but not in the previous ones. Define -// the values here until we're sure everyone updated their SDK. -#ifndef PROCESS_DEP_ENABLE -#define PROCESS_DEP_ENABLE 0x00000001 -#endif -#ifndef PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION -#define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION 0x00000002 -#endif - -// SetProcessDEPPolicy is declared in the Windows 2008 SDK. -typedef BOOL (WINAPI *FnSetProcessDEPPolicy)(DWORD dwFlags); - -enum PROCESS_INFORMATION_CLASS { - ProcessExecuteFlags = 0x22, -}; - -// Flags named as per their usage. -const int MEM_EXECUTE_OPTION_ENABLE = 1; -const int MEM_EXECUTE_OPTION_DISABLE = 2; -const int MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION = 4; -const int MEM_EXECUTE_OPTION_PERMANENT = 8; - -// Not exactly the right signature but that will suffice. -typedef HRESULT (WINAPI *FnNtSetInformationProcess)( - HANDLE ProcessHandle, - PROCESS_INFORMATION_CLASS ProcessInformationClass, - PVOID ProcessInformation, - ULONG ProcessInformationLength); - -} // namespace - -bool SetCurrentProcessDEP(DepEnforcement enforcement) { -#ifdef _WIN64 - // DEP is always on in x64. - return enforcement != DEP_DISABLED; -#endif - // Only available on Windows XP SP2 and Windows Server 2003 SP1. - // For reference: http://www.uninformed.org/?v=2&a=4 - FnNtSetInformationProcess NtSetInformationProc = - reinterpret_cast( - GetProcAddress(GetModuleHandle(L"ntdll.dll"), - "NtSetInformationProcess")); - - if (!NtSetInformationProc) - return false; - - // Flags being used as per SetProcessDEPPolicy on Vista SP1. - ULONG dep_flags; - switch (enforcement) { - case DEP_DISABLED: - // 2 - dep_flags = MEM_EXECUTE_OPTION_DISABLE; - break; - case DEP_ENABLED: - // 9 - dep_flags = MEM_EXECUTE_OPTION_PERMANENT | MEM_EXECUTE_OPTION_ENABLE; - break; - case DEP_ENABLED_ATL7_COMPAT: - // 0xD - dep_flags = MEM_EXECUTE_OPTION_PERMANENT | MEM_EXECUTE_OPTION_ENABLE | - MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION; - break; - default: - NOTREACHED(); - return false; - } - - HRESULT status = NtSetInformationProc(GetCurrentProcess(), - ProcessExecuteFlags, - &dep_flags, - sizeof(dep_flags)); - return SUCCEEDED(status); -} - -} // namespace sandbox diff --git a/sandbox/src/dep.h b/sandbox/src/dep.h deleted file mode 100644 index 9016285..0000000 --- a/sandbox/src/dep.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_DEP_H__ -#define SANDBOX_SRC_DEP_H__ - -namespace sandbox { - -enum DepEnforcement { - // DEP is completely disabled. - DEP_DISABLED, - // DEP is permanently enforced. - DEP_ENABLED, - // DEP with support for ATL7 thunking is permanently enforced. - DEP_ENABLED_ATL7_COMPAT, -}; - -// Change the Data Execution Prevention (DEP) status for the current process. -// Once enabled, it cannot be disabled. -bool SetCurrentProcessDEP(DepEnforcement enforcement); - -} // namespace sandbox - -#endif // SANDBOX_SRC_DEP_H__ diff --git a/sandbox/src/dep_test.cc b/sandbox/src/dep_test.cc deleted file mode 100644 index 91d4e67..0000000 --- a/sandbox/src/dep_test.cc +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/dep.h" - -#include "sandbox/src/sandbox_utils.h" -#include "sandbox/tests/common/controller.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -namespace { - -BYTE kReturnCode[] = { - // ret - 0xC3, -}; - -typedef void (*NullFunction)(); - -// This doesn't fail on Vista Service Pack 0 but it does on XP SP2 and Vista -// SP1. I guess this is a bug in Vista SP0 w.r.t .data PE section. Needs -// investigation to be sure it is a bug and not an error on my part. -bool GenerateDepException() { - bool result = false; - __try { - void* code = kReturnCode; - // Call this code. - reinterpret_cast(code)(); - } __except(EXCEPTION_EXECUTE_HANDLER) { - result = true; - } - return result; -} - -bool GenerateDepAtl7Exception() { - // TODO(maruel): bug 1207762 Somehow test ATL7 - return GenerateDepException(); -} - -SBOX_TESTS_COMMAND int CheckDepLevel(int argc, wchar_t **argv) { - if (1 != argc) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - int flag = _wtoi(argv[0]); - switch (flag) { - case 1: - // DEP is completely disabled. - if (!SetCurrentProcessDEP(DEP_DISABLED)) { - if (!IsXPSP2OrLater()) - // That's fine. - return SBOX_TEST_SUCCEEDED; - return SBOX_TEST_DENIED; - } - if (GenerateDepException()) - return SBOX_TEST_FAILED; - if (GenerateDepAtl7Exception()) - return SBOX_TEST_FAILED; - return SBOX_TEST_SUCCEEDED; - case 2: - // DEP is enabled with ATL7 thunk support. - if (!SetCurrentProcessDEP(DEP_ENABLED_ATL7_COMPAT)) { - if (!IsXPSP2OrLater()) - // That's fine. - return SBOX_TEST_SUCCEEDED; - return SBOX_TEST_DENIED; - } - if (!GenerateDepException()) - return SBOX_TEST_FAILED; - if (GenerateDepAtl7Exception()) - return SBOX_TEST_FAILED; - return SBOX_TEST_SUCCEEDED; - case 3: - // DEP is enabled. - if (!SetCurrentProcessDEP(DEP_ENABLED)) { - if (!IsXPSP2OrLater()) - // That's fine. - return SBOX_TEST_SUCCEEDED; - return SBOX_TEST_DENIED; - } - if (!GenerateDepException()) - return SBOX_TEST_FAILED; - if (!GenerateDepAtl7Exception()) - return SBOX_TEST_FAILED; - return SBOX_TEST_SUCCEEDED; - case 4: - // DEP can't be disabled. - if (!SetCurrentProcessDEP(DEP_ENABLED)) { - if (!IsXPSP2OrLater()) - // That's fine. - return SBOX_TEST_SUCCEEDED; - } - if (SetCurrentProcessDEP(DEP_DISABLED)) { - return SBOX_TEST_DENIED; - } - // Verify that it is still enabled. - if (!GenerateDepException()) - return SBOX_TEST_FAILED; - if (!GenerateDepAtl7Exception()) - return SBOX_TEST_FAILED; - return SBOX_TEST_SUCCEEDED; - case 5: - // DEP can't be disabled. - if (!SetCurrentProcessDEP(DEP_ENABLED_ATL7_COMPAT)) { - if (!IsXPSP2OrLater()) - // That's fine. - return SBOX_TEST_SUCCEEDED; - } - if (SetCurrentProcessDEP(DEP_DISABLED)) { - return SBOX_TEST_DENIED; - } - // Verify that it is still enabled. - if (!GenerateDepException()) - return SBOX_TEST_FAILED; - if (!GenerateDepAtl7Exception()) - return SBOX_TEST_FAILED; - return SBOX_TEST_SUCCEEDED; - case 6: - // DEP can't be disabled. - if (!SetCurrentProcessDEP(DEP_ENABLED)) { - if (!IsXPSP2OrLater()) - // That's fine. - return SBOX_TEST_SUCCEEDED; - } - if (SetCurrentProcessDEP(DEP_ENABLED_ATL7_COMPAT)) { - return SBOX_TEST_DENIED; - } - // Verify that it is still enabled. - if (!GenerateDepException()) - return SBOX_TEST_FAILED; - if (!GenerateDepAtl7Exception()) - return SBOX_TEST_FAILED; - return SBOX_TEST_SUCCEEDED; - default: - return SBOX_TEST_INVALID_PARAMETER; - } - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; -} - -} // namespace - -// This test is disabled. See bug 1275842 -TEST(DepTest, DISABLED_TestDepDisable) { - TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE); - - runner.SetTimeout(INFINITE); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 1")); - // TODO(maruel): bug 1207762 Somehow test ATL7 - // EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 2")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 3")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 4")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 5")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 6")); -} - -} // namespace sandbox diff --git a/sandbox/src/eat_resolver.cc b/sandbox/src/eat_resolver.cc deleted file mode 100644 index f057006..0000000 --- a/sandbox/src/eat_resolver.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/eat_resolver.h" - -#include "base/win/pe_image.h" -#include "sandbox/src/sandbox_nt_util.h" - -namespace sandbox { - -NTSTATUS EatResolverThunk::Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) { - NTSTATUS ret = Init(target_module, interceptor_module, target_name, - interceptor_name, interceptor_entry_point, - thunk_storage, storage_bytes); - if (!NT_SUCCESS(ret)) - return ret; - - if (!eat_entry_) - return STATUS_INVALID_PARAMETER; - - size_t thunk_bytes = GetInternalThunkSize(); - -#if defined(_WIN64) - // We have two thunks, in order: the return path and the forward path. - if (!SetInternalThunk(thunk_storage, storage_bytes, NULL, target_)) - return STATUS_BUFFER_TOO_SMALL; - - storage_bytes -= thunk_bytes; - thunk_storage = reinterpret_cast(thunk_storage) + thunk_bytes; -#endif - - if (!SetInternalThunk(thunk_storage, storage_bytes, target_, interceptor_)) - return STATUS_BUFFER_TOO_SMALL; - - AutoProtectMemory memory; - memory.ChangeProtection(eat_entry_, sizeof(DWORD), PAGE_READWRITE); - - // Perform the patch. -#pragma warning(push) -#pragma warning(disable: 4311) - // These casts generate warnings because they are 32 bit specific. - *eat_entry_ = reinterpret_cast(thunk_storage) - - reinterpret_cast(target_module); -#pragma warning(pop) - - if (NULL != storage_used) - *storage_used = GetThunkSize(); - - return ret; -} - -NTSTATUS EatResolverThunk::ResolveTarget(const void* module, - const char* function_name, - void** address) { - DCHECK_NT(address); - if (!module) - return STATUS_INVALID_PARAMETER; - - base::win::PEImage pe(module); - if (!pe.VerifyMagic()) - return STATUS_INVALID_IMAGE_FORMAT; - - eat_entry_ = pe.GetExportEntry(function_name); - - if (!eat_entry_) - return STATUS_PROCEDURE_NOT_FOUND; - - *address = pe.RVAToAddr(*eat_entry_); - - return STATUS_SUCCESS; -} - -size_t EatResolverThunk::GetThunkSize() const { -#if defined(_WIN64) - return GetInternalThunkSize() * 2; -#else - return GetInternalThunkSize(); -#endif -} - -} // namespace sandbox diff --git a/sandbox/src/eat_resolver.h b/sandbox/src/eat_resolver.h deleted file mode 100644 index 0d5b3e0..0000000 --- a/sandbox/src/eat_resolver.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_EAT_RESOLVER_H__ -#define SANDBOX_SRC_EAT_RESOLVER_H__ - -#include "base/basictypes.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/resolver.h" - -namespace sandbox { - -// This is the concrete resolver used to perform exports table interceptions. -class EatResolverThunk : public ResolverThunk { - public: - EatResolverThunk() : eat_entry_(NULL) {} - virtual ~EatResolverThunk() {} - - // Implementation of Resolver::Setup. - virtual NTSTATUS Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used); - - // Implementation of Resolver::ResolveTarget. - virtual NTSTATUS ResolveTarget(const void* module, - const char* function_name, - void** address); - - // Implementation of Resolver::GetThunkSize. - virtual size_t GetThunkSize() const; - - private: - // The entry to patch. - DWORD* eat_entry_; - - DISALLOW_COPY_AND_ASSIGN(EatResolverThunk); -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_EAT_RESOLVER_H__ diff --git a/sandbox/src/file_policy_test.cc b/sandbox/src/file_policy_test.cc deleted file mode 100644 index df1e903..0000000 --- a/sandbox/src/file_policy_test.cc +++ /dev/null @@ -1,598 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include -#include - -#include "base/win/scoped_handle.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/win_utils.h" -#include "sandbox/tests/common/controller.h" -#include "sandbox/tests/common/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -#define BINDNTDLL(name) \ - name ## Function name = reinterpret_cast( \ - ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) - -namespace sandbox { - -const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE; - -// Creates a file using different desired access. Returns if the call succeeded -// or not. The first argument in argv is the filename. If the second argument -// is "read", we try read only access. Otherwise we try read-write access. -SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t **argv) { - if (argc != 2) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - bool read = (_wcsicmp(argv[0], L"Read") == 0); - - if (read) { - base::win::ScopedHandle file1(CreateFile( - argv[1], GENERIC_READ, kSharing, NULL, OPEN_EXISTING, 0, NULL)); - base::win::ScopedHandle file2(CreateFile( - argv[1], FILE_EXECUTE, kSharing, NULL, OPEN_EXISTING, 0, NULL)); - - if (file1.Get() && file2.Get()) - return SBOX_TEST_SUCCEEDED; - return SBOX_TEST_DENIED; - } else { - base::win::ScopedHandle file1(CreateFile( - argv[1], GENERIC_ALL, kSharing, NULL, OPEN_EXISTING, 0, NULL)); - base::win::ScopedHandle file2(CreateFile( - argv[1], GENERIC_READ | FILE_WRITE_DATA, kSharing, NULL, OPEN_EXISTING, - 0, NULL)); - - if (file1.Get() && file2.Get()) - return SBOX_TEST_SUCCEEDED; - return SBOX_TEST_DENIED; - } -} - -SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) { - if (argc != 1) { - SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - - std::wstring full_path = MakePathToSys(argv[0], false); - if (full_path.empty()) { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - - HANDLE file = ::CreateFileW(full_path.c_str(), GENERIC_READ, kSharing, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if (INVALID_HANDLE_VALUE != file) { - ::CloseHandle(file); - return SBOX_TEST_SUCCEEDED; - } else { - if (ERROR_ACCESS_DENIED == ::GetLastError()) { - return SBOX_TEST_DENIED; - } else { - return SBOX_TEST_FAILED; - } - } - return SBOX_TEST_SUCCEEDED; -} - -// Creates the file in parameter using the NtCreateFile api and returns if the -// call succeeded or not. -SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) { - BINDNTDLL(NtCreateFile); - BINDNTDLL(RtlInitUnicodeString); - if (!NtCreateFile || !RtlInitUnicodeString) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - if (argc != 1) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - std::wstring file(argv[0]); - if (0 != _wcsnicmp(file.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) - file = MakePathToSys(argv[0], true); - - UNICODE_STRING object_name; - RtlInitUnicodeString(&object_name, file.c_str()); - - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitializeObjectAttributes(&obj_attributes, &object_name, - OBJ_CASE_INSENSITIVE, NULL, NULL); - - HANDLE handle; - IO_STATUS_BLOCK io_block = {0}; - NTSTATUS status = NtCreateFile(&handle, FILE_READ_DATA, &obj_attributes, - &io_block, NULL, 0, kSharing, FILE_OPEN, - 0, NULL, 0); - if (NT_SUCCESS(status)) { - ::CloseHandle(handle); - return SBOX_TEST_SUCCEEDED; - } else if (STATUS_ACCESS_DENIED == status) { - return SBOX_TEST_DENIED; - } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { - return SBOX_TEST_NOT_FOUND; - } - return SBOX_TEST_FAILED; -} - -// Opens the file in parameter using the NtOpenFile api and returns if the -// call succeeded or not. -SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { - BINDNTDLL(NtOpenFile); - BINDNTDLL(RtlInitUnicodeString); - if (!NtOpenFile || !RtlInitUnicodeString) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - if (argc != 1) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - std::wstring file = MakePathToSys(argv[0], true); - UNICODE_STRING object_name; - RtlInitUnicodeString(&object_name, file.c_str()); - - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitializeObjectAttributes(&obj_attributes, &object_name, - OBJ_CASE_INSENSITIVE, NULL, NULL); - - HANDLE handle; - IO_STATUS_BLOCK io_block = {0}; - NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes, - &io_block, kSharing, 0); - if (NT_SUCCESS(status)) { - ::CloseHandle(handle); - return SBOX_TEST_SUCCEEDED; - } else if (STATUS_ACCESS_DENIED == status) { - return SBOX_TEST_DENIED; - } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { - return SBOX_TEST_NOT_FOUND; - } - return SBOX_TEST_FAILED; -} - -SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t **argv) { - std::wstring sys_path = MakePathToSys(L"", false); - if (sys_path.empty()) { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - ULARGE_INTEGER free_user = {0}; - ULARGE_INTEGER total = {0}; - ULARGE_INTEGER free_total = {0}; - if (::GetDiskFreeSpaceExW(sys_path.c_str(), &free_user, &total, - &free_total)) { - if ((total.QuadPart != 0) && (free_total.QuadPart !=0)) { - return SBOX_TEST_SUCCEEDED; - } - } else { - if (ERROR_ACCESS_DENIED == ::GetLastError()) { - return SBOX_TEST_DENIED; - } else { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - } - return SBOX_TEST_SUCCEEDED; -} - -// Move a file using the MoveFileEx api and returns if the call succeeded or -// not. -SBOX_TESTS_COMMAND int File_Rename(int argc, wchar_t **argv) { - if (argc != 2) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - if (::MoveFileEx(argv[0], argv[1], 0)) - return SBOX_TEST_SUCCEEDED; - - if (::GetLastError() != ERROR_ACCESS_DENIED) - return SBOX_TEST_FAILED; - - return SBOX_TEST_DENIED; -} - -// Query the attributes of file in parameter using the NtQueryAttributesFile api -// and NtQueryFullAttributesFile and returns if the call succeeded or not. The -// second argument in argv is "d" or "f" telling if we expect the attributes to -// specify a file or a directory. The expected attribute has to match the real -// attributes for the call to be successful. -SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t **argv) { - BINDNTDLL(NtQueryAttributesFile); - BINDNTDLL(NtQueryFullAttributesFile); - BINDNTDLL(RtlInitUnicodeString); - if (!NtQueryAttributesFile || !NtQueryFullAttributesFile || - !RtlInitUnicodeString) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - if (argc != 2) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - bool expect_directory = (L'd' == argv[1][0]); - - UNICODE_STRING object_name; - std::wstring file = MakePathToSys(argv[0], true); - RtlInitUnicodeString(&object_name, file.c_str()); - - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitializeObjectAttributes(&obj_attributes, &object_name, - OBJ_CASE_INSENSITIVE, NULL, NULL); - - FILE_BASIC_INFORMATION info = {0}; - FILE_NETWORK_OPEN_INFORMATION full_info = {0}; - NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info); - NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info); - - if (status1 != status2) - return SBOX_TEST_FAILED; - - if (NT_SUCCESS(status1)) { - if (info.FileAttributes != full_info.FileAttributes) - return SBOX_TEST_FAILED; - - bool is_directory1 = (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - if (expect_directory == is_directory1) - return SBOX_TEST_SUCCEEDED; - } else if (STATUS_ACCESS_DENIED == status1) { - return SBOX_TEST_DENIED; - } else if (STATUS_OBJECT_NAME_NOT_FOUND == status1) { - return SBOX_TEST_NOT_FOUND; - } - - return SBOX_TEST_FAILED; -} - -TEST(FilePolicyTest, DenyNtCreateCalc) { - TestRunner runner; - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, - L"calc.exe")); - - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 calc.exe")); - - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); -} - -TEST(FilePolicyTest, AllowNtCreateCalc) { - TestRunner runner; - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); - - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); -} - -TEST(FilePolicyTest, AllowNtCreateWithNativePath) { - std::wstring calc = MakePathToSys(L"calc.exe", false); - std::wstring nt_path; - ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path)); - TestRunner runner; - runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str()); - - wchar_t buff[MAX_PATH]; - ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff)); - - std::transform(nt_path.begin(), nt_path.end(), nt_path.begin(), std::tolower); - ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff)); -} - -TEST(FilePolicyTest, AllowReadOnly) { - TestRunner runner; - - // Create a temp file because we need write access to it. - wchar_t temp_directory[MAX_PATH]; - wchar_t temp_file_name[MAX_PATH]; - ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); - - EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, - temp_file_name)); - - wchar_t command_read[MAX_PATH + 20] = {0}; - wsprintf(command_read, L"File_Create Read \"%ls\"", temp_file_name); - wchar_t command_write[MAX_PATH + 20] = {0}; - wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name); - - // Verify that we have read access after revert. - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_read)); - - // Verify that we don't have write access after revert. - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write)); - - // Verify that we really have write access to the file. - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write)); - - DeleteFile(temp_file_name); -} - -TEST(FilePolicyTest, AllowWildcard) { - TestRunner runner; - - // Create a temp file because we need write access to it. - wchar_t temp_directory[MAX_PATH]; - wchar_t temp_file_name[MAX_PATH]; - ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); - - wcscat_s(temp_directory, MAX_PATH, L"*"); - EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_directory)); - - wchar_t command_write[MAX_PATH + 20] = {0}; - wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name); - - // Verify that we have write access after revert. - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write)); - - DeleteFile(temp_file_name); -} - -TEST(FilePolicyTest, AllowNtCreatePatternRule) { - TestRunner runner; - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"App*.dll")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_OpenSys32 appmgmts.dll")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_OpenSys32 appwiz.cpl")); - - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_OpenSys32 appmgmts.dll")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_OpenSys32 appwiz.cpl")); -} - -TEST(FilePolicyTest, CheckNotFound) { - TestRunner runner; - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"n*.dll")); - - EXPECT_EQ(SBOX_TEST_NOT_FOUND, - runner.RunTest(L"File_OpenSys32 notfound.dll")); -} - -TEST(FilePolicyTest, CheckNoLeak) { - TestRunner runner; - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 notfound.exe")); -} - -TEST(FilePolicyTest, TestQueryAttributesFile) { - TestRunner runner; - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, - L"appmgmts.dll")); - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, - L"notfound.exe")); - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"drivers")); - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_QUERY, - L"ipconfig.exe")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_QueryAttributes drivers d")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_QueryAttributes appmgmts.dll f")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_QueryAttributes ipconfig.exe f")); - - EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"File_QueryAttributes ftp.exe f")); - - EXPECT_EQ(SBOX_TEST_NOT_FOUND, - runner.RunTest(L"File_QueryAttributes notfound.exe f")); -} - -// Makes sure that we don't leak information when there is not policy to allow -// a path. -TEST(FilePolicyTest, TestQueryAttributesFileNoPolicy) { - TestRunner runner; - EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"File_QueryAttributes ftp.exe f")); - - EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"File_QueryAttributes notfound.exe f")); -} - -TEST(FilePolicyTest, TestRename) { - TestRunner runner; - - // Give access to the temp directory. - wchar_t temp_directory[MAX_PATH]; - wchar_t temp_file_name1[MAX_PATH]; - wchar_t temp_file_name2[MAX_PATH]; - wchar_t temp_file_name3[MAX_PATH]; - wchar_t temp_file_name4[MAX_PATH]; - wchar_t temp_file_name5[MAX_PATH]; - wchar_t temp_file_name6[MAX_PATH]; - wchar_t temp_file_name7[MAX_PATH]; - wchar_t temp_file_name8[MAX_PATH]; - ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name1), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name2), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name3), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name4), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name5), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name6), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name7), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name8), 0u); - - - // Add rules to make file1->file2 succeed. - ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name1)); - ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name2)); - - // Add rules to make file3->file4 fail. - ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name3)); - ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, - temp_file_name4)); - - // Add rules to make file5->file6 fail. - ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, - temp_file_name5)); - ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name6)); - - // Add rules to make file7->no_pol_file fail. - ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name7)); - - // Delete the files where the files are going to be renamed to. - ::DeleteFile(temp_file_name2); - ::DeleteFile(temp_file_name4); - ::DeleteFile(temp_file_name6); - ::DeleteFile(temp_file_name8); - - - wchar_t command[MAX_PATH*2 + 20] = {0}; - wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name1, - temp_file_name2); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command)); - - wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name3, - temp_file_name4); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); - - wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name5, - temp_file_name6); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); - - wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name7, - temp_file_name8); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); - - - // Delete all the files in case they are still there. - ::DeleteFile(temp_file_name1); - ::DeleteFile(temp_file_name2); - ::DeleteFile(temp_file_name3); - ::DeleteFile(temp_file_name4); - ::DeleteFile(temp_file_name5); - ::DeleteFile(temp_file_name6); - ::DeleteFile(temp_file_name7); - ::DeleteFile(temp_file_name8); -} - -TEST(FilePolicyTest, OpenSys32FilesDenyBecauseOfDir) { - TestRunner runner; - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, - L"notepad.exe")); - - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe")); - - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_Win32Create notepad.exe")); -} - -TEST(FilePolicyTest, OpenSys32FilesAllowNotepad) { - TestRunner runner; - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, - L"notepad.exe")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_Win32Create notepad.exe")); - - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create calc.exe")); - - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_Win32Create notepad.exe")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_Win32Create calc.exe")); -} - -TEST(FilePolicyTest, FileGetDiskSpace) { - TestRunner runner; - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_GetDiskSpace")); - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace")); - - // Add an 'allow' rule in the windows\system32 such that GetDiskFreeSpaceEx - // succeeds (it does an NtOpenFile) but windows\system32\notepad.exe is - // denied since there is no wild card in the rule. - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"")); - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace")); - - runner.SetTestState(AFTER_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe")); -} - -TEST(FilePolicyTest, TestReparsePoint) { - TestRunner runner; - - // Create a temp file because we need write access to it. - wchar_t temp_directory[MAX_PATH]; - wchar_t temp_file_name[MAX_PATH]; - ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); - - // Delete the file and create a directory instead. - ASSERT_TRUE(::DeleteFile(temp_file_name)); - ASSERT_TRUE(::CreateDirectory(temp_file_name, NULL)); - - // Create a temporary file in the subfolder. - std::wstring subfolder = temp_file_name; - std::wstring temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1); - std::wstring temp_file = subfolder + L"\\file_" + temp_file_title; - - HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - CREATE_ALWAYS, 0, NULL); - ASSERT_TRUE(INVALID_HANDLE_VALUE != file); - ASSERT_TRUE(::CloseHandle(file)); - - // Create a temporary file in the temp directory. - std::wstring temp_dir = temp_directory; - std::wstring temp_file_in_temp = temp_dir + L"file_" + temp_file_title; - file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - CREATE_ALWAYS, 0, NULL); - ASSERT_TRUE(file != NULL); - ASSERT_TRUE(::CloseHandle(file)); - - // Give write access to the temp directory. - std::wstring temp_dir_wildcard = temp_dir + L"*"; - EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, - temp_dir_wildcard.c_str())); - - // Prepare the command to execute. - std::wstring command_write; - command_write += L"File_Create Write \""; - command_write += temp_file; - command_write += L"\""; - - // Verify that we have write access to the original file - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str())); - - // Replace the subfolder by a reparse point to %temp%. - ::DeleteFile(temp_file.c_str()); - HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); - - std::wstring temp_dir_nt; - temp_dir_nt += L"\\??\\"; - temp_dir_nt += temp_dir; - EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); - EXPECT_TRUE(::CloseHandle(dir)); - - // Try to open the file again. - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str())); - - // Remove the reparse point. - dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, - NULL); - EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); - EXPECT_TRUE(DeleteReparsePoint(dir)); - EXPECT_TRUE(::CloseHandle(dir)); - - // Cleanup. - EXPECT_TRUE(::DeleteFile(temp_file_in_temp.c_str())); - EXPECT_TRUE(::RemoveDirectory(subfolder.c_str())); -} - -} // namespace sandbox diff --git a/sandbox/src/filesystem_dispatcher.cc b/sandbox/src/filesystem_dispatcher.cc deleted file mode 100644 index 71a6f02..0000000 --- a/sandbox/src/filesystem_dispatcher.cc +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/filesystem_dispatcher.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/filesystem_interception.h" -#include "sandbox/src/filesystem_policy.h" -#include "sandbox/src/interception.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_broker.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_nt_util.h" - -namespace sandbox { - -FilesystemDispatcher::FilesystemDispatcher(PolicyBase* policy_base) - : policy_base_(policy_base) { - static const IPCCall create_params = { - {IPC_NTCREATEFILE_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE, - ULONG_TYPE, ULONG_TYPE, ULONG_TYPE}, - reinterpret_cast(&FilesystemDispatcher::NtCreateFile) - }; - - static const IPCCall open_file = { - {IPC_NTOPENFILE_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE, - ULONG_TYPE}, - reinterpret_cast(&FilesystemDispatcher::NtOpenFile) - }; - - static const IPCCall attribs = { - {IPC_NTQUERYATTRIBUTESFILE_TAG, WCHAR_TYPE, ULONG_TYPE, INOUTPTR_TYPE}, - reinterpret_cast( - &FilesystemDispatcher::NtQueryAttributesFile) - }; - - static const IPCCall full_attribs = { - {IPC_NTQUERYFULLATTRIBUTESFILE_TAG, WCHAR_TYPE, ULONG_TYPE, INOUTPTR_TYPE}, - reinterpret_cast( - &FilesystemDispatcher::NtQueryFullAttributesFile) - }; - - static const IPCCall set_info = { - {IPC_NTSETINFO_RENAME_TAG, VOIDPTR_TYPE, INOUTPTR_TYPE, INOUTPTR_TYPE, - ULONG_TYPE, ULONG_TYPE}, - reinterpret_cast( - &FilesystemDispatcher::NtSetInformationFile) - }; - - ipc_calls_.push_back(create_params); - ipc_calls_.push_back(open_file); - ipc_calls_.push_back(attribs); - ipc_calls_.push_back(full_attribs); - ipc_calls_.push_back(set_info); -} - -bool FilesystemDispatcher::SetupService(InterceptionManager* manager, - int service) { - switch (service) { - case IPC_NTCREATEFILE_TAG: - return INTERCEPT_NT(manager, NtCreateFile, CREATE_FILE_ID, 48); - - case IPC_NTOPENFILE_TAG: - return INTERCEPT_NT(manager, NtOpenFile, OPEN_FILE_ID, 28); - - case IPC_NTQUERYATTRIBUTESFILE_TAG: - return INTERCEPT_NT(manager, NtQueryAttributesFile, QUERY_ATTRIB_FILE_ID, - 12); - - case IPC_NTQUERYFULLATTRIBUTESFILE_TAG: - return INTERCEPT_NT(manager, NtQueryFullAttributesFile, - QUERY_FULL_ATTRIB_FILE_ID, 12); - - case IPC_NTSETINFO_RENAME_TAG: - return INTERCEPT_NT(manager, NtSetInformationFile, SET_INFO_FILE_ID, 24); - - default: - return false; - } -} - -bool FilesystemDispatcher::NtCreateFile( - IPCInfo* ipc, std::wstring* name, DWORD attributes, DWORD desired_access, - DWORD file_attributes, DWORD share_access, DWORD create_disposition, - DWORD create_options) { - if (!PreProcessName(*name, name)) { - // The path requested might contain a reparse point. - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - const wchar_t* filename = name->c_str(); - - ULONG broker = TRUE; - CountedParameterSet params; - params[OpenFile::NAME] = ParamPickerMake(filename); - params[OpenFile::ACCESS] = ParamPickerMake(desired_access); - params[OpenFile::OPTIONS] = ParamPickerMake(create_options); - params[OpenFile::BROKER] = ParamPickerMake(broker); - - // To evaluate the policy we need to call back to the policy object. We - // are just middlemen in the operation since is the FileSystemPolicy which - // knows what to do. - EvalResult result = policy_base_->EvalPolicy(IPC_NTCREATEFILE_TAG, - params.GetBase()); - HANDLE handle; - ULONG_PTR io_information = 0; - NTSTATUS nt_status; - if (!FileSystemPolicy::CreateFileAction(result, *ipc->client_info, *name, - attributes, desired_access, - file_attributes, share_access, - create_disposition, create_options, - &handle, &nt_status, - &io_information)) { - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - // Return operation status on the IPC. - ipc->return_info.extended[0].ulong_ptr = io_information; - ipc->return_info.nt_status = nt_status; - ipc->return_info.handle = handle; - return true; -} - -bool FilesystemDispatcher::NtOpenFile( - IPCInfo* ipc, std::wstring* name, DWORD attributes, DWORD desired_access, - DWORD share_access, DWORD open_options) { - if (!PreProcessName(*name, name)) { - // The path requested might contain a reparse point. - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - const wchar_t* filename = name->c_str(); - - ULONG broker = TRUE; - CountedParameterSet params; - params[OpenFile::NAME] = ParamPickerMake(filename); - params[OpenFile::ACCESS] = ParamPickerMake(desired_access); - params[OpenFile::OPTIONS] = ParamPickerMake(open_options); - params[OpenFile::BROKER] = ParamPickerMake(broker); - - // To evaluate the policy we need to call back to the policy object. We - // are just middlemen in the operation since is the FileSystemPolicy which - // knows what to do. - EvalResult result = policy_base_->EvalPolicy(IPC_NTOPENFILE_TAG, - params.GetBase()); - HANDLE handle; - ULONG_PTR io_information = 0; - NTSTATUS nt_status; - if (!FileSystemPolicy::OpenFileAction(result, *ipc->client_info, *name, - attributes, desired_access, - share_access, open_options, &handle, - &nt_status, &io_information)) { - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - // Return operation status on the IPC. - ipc->return_info.extended[0].ulong_ptr = io_information; - ipc->return_info.nt_status = nt_status; - ipc->return_info.handle = handle; - return true; -} - -bool FilesystemDispatcher::NtQueryAttributesFile( - IPCInfo* ipc, std::wstring* name, DWORD attributes, CountedBuffer* info) { - if (sizeof(FILE_BASIC_INFORMATION) != info->Size()) - return false; - - if (!PreProcessName(*name, name)) { - // The path requested might contain a reparse point. - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - ULONG broker = TRUE; - const wchar_t* filename = name->c_str(); - CountedParameterSet params; - params[FileName::NAME] = ParamPickerMake(filename); - params[FileName::BROKER] = ParamPickerMake(broker); - - // To evaluate the policy we need to call back to the policy object. We - // are just middlemen in the operation since is the FileSystemPolicy which - // knows what to do. - EvalResult result = policy_base_->EvalPolicy(IPC_NTQUERYATTRIBUTESFILE_TAG, - params.GetBase()); - - FILE_BASIC_INFORMATION* information = - reinterpret_cast(info->Buffer()); - NTSTATUS nt_status; - if (!FileSystemPolicy::QueryAttributesFileAction(result, *ipc->client_info, - *name, attributes, - information, &nt_status)) { - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - // Return operation status on the IPC. - ipc->return_info.nt_status = nt_status; - return true; -} - -bool FilesystemDispatcher::NtQueryFullAttributesFile( - IPCInfo* ipc, std::wstring* name, DWORD attributes, CountedBuffer* info) { - if (sizeof(FILE_NETWORK_OPEN_INFORMATION) != info->Size()) - return false; - - if (!PreProcessName(*name, name)) { - // The path requested might contain a reparse point. - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - ULONG broker = TRUE; - const wchar_t* filename = name->c_str(); - CountedParameterSet params; - params[FileName::NAME] = ParamPickerMake(filename); - params[FileName::BROKER] = ParamPickerMake(broker); - - // To evaluate the policy we need to call back to the policy object. We - // are just middlemen in the operation since is the FileSystemPolicy which - // knows what to do. - EvalResult result = policy_base_->EvalPolicy( - IPC_NTQUERYFULLATTRIBUTESFILE_TAG, params.GetBase()); - - FILE_NETWORK_OPEN_INFORMATION* information = - reinterpret_cast(info->Buffer()); - NTSTATUS nt_status; - if (!FileSystemPolicy::QueryFullAttributesFileAction(result, - *ipc->client_info, - *name, attributes, - information, - &nt_status)) { - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - // Return operation status on the IPC. - ipc->return_info.nt_status = nt_status; - return true; -} - -bool FilesystemDispatcher::NtSetInformationFile( - IPCInfo* ipc, HANDLE handle, CountedBuffer* status, CountedBuffer* info, - DWORD length, DWORD info_class) { - if (sizeof(IO_STATUS_BLOCK) != status->Size()) - return false; - if (length != info->Size()) - return false; - - FILE_RENAME_INFORMATION* rename_info = - reinterpret_cast(info->Buffer()); - - if (!IsSupportedRenameCall(rename_info, length, info_class)) - return false; - - std::wstring name; - name.assign(rename_info->FileName, rename_info->FileNameLength / - sizeof(rename_info->FileName[0])); - if (!PreProcessName(name, &name)) { - // The path requested might contain a reparse point. - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - ULONG broker = TRUE; - const wchar_t* filename = name.c_str(); - CountedParameterSet params; - params[FileName::NAME] = ParamPickerMake(filename); - params[FileName::BROKER] = ParamPickerMake(broker); - - // To evaluate the policy we need to call back to the policy object. We - // are just middlemen in the operation since is the FileSystemPolicy which - // knows what to do. - EvalResult result = policy_base_->EvalPolicy(IPC_NTSETINFO_RENAME_TAG, - params.GetBase()); - - IO_STATUS_BLOCK* io_status = - reinterpret_cast(status->Buffer()); - NTSTATUS nt_status; - if (!FileSystemPolicy::SetInformationFileAction(result, *ipc->client_info, - handle, rename_info, length, - info_class, io_status, - &nt_status)) { - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - // Return operation status on the IPC. - ipc->return_info.nt_status = nt_status; - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/filesystem_dispatcher.h b/sandbox/src/filesystem_dispatcher.h deleted file mode 100644 index d828715..0000000 --- a/sandbox/src/filesystem_dispatcher.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__ -#define SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__ - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sandbox_policy_base.h" - -namespace sandbox { - -// This class handles file system-related IPC calls. -class FilesystemDispatcher : public Dispatcher { - public: - explicit FilesystemDispatcher(PolicyBase* policy_base); - ~FilesystemDispatcher() {} - - // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); - - private: - // Processes IPC requests coming from calls to NtCreateFile in the target. - bool NtCreateFile(IPCInfo* ipc, std::wstring* name, DWORD attributes, - DWORD desired_access, DWORD file_attributes, - DWORD share_access, DWORD create_disposition, - DWORD create_options); - - // Processes IPC requests coming from calls to NtOpenFile in the target. - bool NtOpenFile(IPCInfo* ipc, std::wstring* name, DWORD attributes, - DWORD desired_access, DWORD share_access, - DWORD create_options); - - // Processes IPC requests coming from calls to NtQueryAttributesFile in the - // target. - bool NtQueryAttributesFile(IPCInfo* ipc, std::wstring* name, DWORD attributes, - CountedBuffer* info); - - // Processes IPC requests coming from calls to NtQueryFullAttributesFile in - // the target. - bool NtQueryFullAttributesFile(IPCInfo* ipc, std::wstring* name, - DWORD attributes, CountedBuffer* info); - - // Processes IPC requests coming from calls to NtSetInformationFile with the - // rename information class. - bool NtSetInformationFile(IPCInfo* ipc, HANDLE handle, CountedBuffer* status, - CountedBuffer* info, DWORD length, - DWORD info_class); - - PolicyBase* policy_base_; - DISALLOW_COPY_AND_ASSIGN(FilesystemDispatcher); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__ diff --git a/sandbox/src/filesystem_interception.cc b/sandbox/src/filesystem_interception.cc deleted file mode 100644 index cdc10ff..0000000 --- a/sandbox/src/filesystem_interception.cc +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/filesystem_interception.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/policy_target.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/target_services.h" - -namespace sandbox { - -NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile, - PHANDLE file, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, - PIO_STATUS_BLOCK io_status, - PLARGE_INTEGER allocation_size, - ULONG file_attributes, ULONG sharing, - ULONG disposition, ULONG options, - PVOID ea_buffer, ULONG ea_length) { - // Check if the process can open it first. - NTSTATUS status = orig_CreateFile(file, desired_access, object_attributes, - io_status, allocation_size, - file_attributes, sharing, disposition, - options, ea_buffer, ea_length); - if (STATUS_ACCESS_DENIED != status) - return status; - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return status; - - do { - if (!ValidParameter(file, sizeof(HANDLE), WRITE)) - break; - if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - wchar_t* name; - uint32 attributes = 0; - NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, - NULL); - if (!NT_SUCCESS(ret) || NULL == name) - break; - - ULONG broker = FALSE; - CountedParameterSet params; - params[OpenFile::NAME] = ParamPickerMake(name); - params[OpenFile::ACCESS] = ParamPickerMake(desired_access); - params[OpenFile::OPTIONS] = ParamPickerMake(options); - params[OpenFile::BROKER] = ParamPickerMake(broker); - - if (!QueryBroker(IPC_NTCREATEFILE_TAG, params.GetBase())) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - // The following call must match in the parameters with - // FilesystemDispatcher::ProcessNtCreateFile. - ResultCode code = CrossCall(ipc, IPC_NTCREATEFILE_TAG, name, attributes, - desired_access, file_attributes, sharing, - disposition, options, &answer); - - operator delete(name, NT_ALLOC); - - if (SBOX_ALL_OK != code) - break; - - if (!NT_SUCCESS(answer.nt_status)) - return answer.nt_status; - - __try { - *file = answer.handle; - io_status->Status = answer.nt_status; - io_status->Information = answer.extended[0].ulong_ptr; - status = io_status->Status; - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - } while (false); - - return status; -} - -NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile, PHANDLE file, - ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, - PIO_STATUS_BLOCK io_status, ULONG sharing, - ULONG options) { - // Check if the process can open it first. - NTSTATUS status = orig_OpenFile(file, desired_access, object_attributes, - io_status, sharing, options); - if (STATUS_ACCESS_DENIED != status) - return status; - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return status; - - do { - if (!ValidParameter(file, sizeof(HANDLE), WRITE)) - break; - if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - wchar_t* name; - uint32 attributes; - NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, - NULL); - if (!NT_SUCCESS(ret) || NULL == name) - break; - - ULONG broker = FALSE; - CountedParameterSet params; - params[OpenFile::NAME] = ParamPickerMake(name); - params[OpenFile::ACCESS] = ParamPickerMake(desired_access); - params[OpenFile::OPTIONS] = ParamPickerMake(options); - params[OpenFile::BROKER] = ParamPickerMake(broker); - - if (!QueryBroker(IPC_NTOPENFILE_TAG, params.GetBase())) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_NTOPENFILE_TAG, name, attributes, - desired_access, sharing, options, &answer); - - operator delete(name, NT_ALLOC); - - if (SBOX_ALL_OK != code) - break; - - if (!NT_SUCCESS(answer.nt_status)) - return answer.nt_status; - - __try { - *file = answer.handle; - io_status->Status = answer.nt_status; - io_status->Information = answer.extended[0].ulong_ptr; - status = io_status->Status; - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - } while (false); - - return status; -} - -NTSTATUS WINAPI TargetNtQueryAttributesFile( - NtQueryAttributesFileFunction orig_QueryAttributes, - POBJECT_ATTRIBUTES object_attributes, - PFILE_BASIC_INFORMATION file_attributes) { - // Check if the process can query it first. - NTSTATUS status = orig_QueryAttributes(object_attributes, file_attributes); - if (STATUS_ACCESS_DENIED != status) - return status; - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return status; - - do { - if (!ValidParameter(file_attributes, sizeof(FILE_BASIC_INFORMATION), WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - wchar_t* name = NULL; - uint32 attributes = 0; - NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, - NULL); - if (!NT_SUCCESS(ret) || NULL == name) - break; - - InOutCountedBuffer file_info(file_attributes, - sizeof(FILE_BASIC_INFORMATION)); - - ULONG broker = FALSE; - CountedParameterSet params; - params[FileName::NAME] = ParamPickerMake(name); - params[FileName::BROKER] = ParamPickerMake(broker); - - if (!QueryBroker(IPC_NTQUERYATTRIBUTESFILE_TAG, params.GetBase())) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_NTQUERYATTRIBUTESFILE_TAG, name, - attributes, file_info, &answer); - - operator delete(name, NT_ALLOC); - - if (SBOX_ALL_OK != code) - break; - - return answer.nt_status; - - } while (false); - - return status; -} - -NTSTATUS WINAPI TargetNtQueryFullAttributesFile( - NtQueryFullAttributesFileFunction orig_QueryFullAttributes, - POBJECT_ATTRIBUTES object_attributes, - PFILE_NETWORK_OPEN_INFORMATION file_attributes) { - // Check if the process can query it first. - NTSTATUS status = orig_QueryFullAttributes(object_attributes, - file_attributes); - if (STATUS_ACCESS_DENIED != status) - return status; - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return status; - - do { - if (!ValidParameter(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION), - WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - wchar_t* name = NULL; - uint32 attributes = 0; - NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, - NULL); - if (!NT_SUCCESS(ret) || NULL == name) - break; - - InOutCountedBuffer file_info(file_attributes, - sizeof(FILE_NETWORK_OPEN_INFORMATION)); - - ULONG broker = FALSE; - CountedParameterSet params; - params[FileName::NAME] = ParamPickerMake(name); - params[FileName::BROKER] = ParamPickerMake(broker); - - if (!QueryBroker(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, params.GetBase())) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_NTQUERYFULLATTRIBUTESFILE_TAG, name, - attributes, file_info, &answer); - - operator delete(name, NT_ALLOC); - - if (SBOX_ALL_OK != code) - break; - - return answer.nt_status; - } while (false); - - return status; -} - -NTSTATUS WINAPI TargetNtSetInformationFile( - NtSetInformationFileFunction orig_SetInformationFile, HANDLE file, - PIO_STATUS_BLOCK io_status, PVOID file_info, ULONG length, - FILE_INFORMATION_CLASS file_info_class) { - // Check if the process can open it first. - NTSTATUS status = orig_SetInformationFile(file, io_status, file_info, length, - file_info_class); - if (STATUS_ACCESS_DENIED != status) - return status; - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return status; - - do { - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE)) - break; - - if (!ValidParameter(file_info, length, READ)) - break; - - FILE_RENAME_INFORMATION* file_rename_info = - reinterpret_cast(file_info); - OBJECT_ATTRIBUTES object_attributes; - UNICODE_STRING object_name; - InitializeObjectAttributes(&object_attributes, &object_name, 0, NULL, NULL); - - __try { - if (!IsSupportedRenameCall(file_rename_info, length, file_info_class)) - break; - - object_attributes.RootDirectory = file_rename_info->RootDirectory; - object_name.Buffer = file_rename_info->FileName; - object_name.Length = object_name.MaximumLength = - static_cast(file_rename_info->FileNameLength); - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - - wchar_t* name; - NTSTATUS ret = AllocAndCopyName(&object_attributes, &name, NULL, NULL); - if (!NT_SUCCESS(ret) || !name) - break; - - ULONG broker = FALSE; - CountedParameterSet params; - params[FileName::NAME] = ParamPickerMake(name); - params[FileName::BROKER] = ParamPickerMake(broker); - - if (!QueryBroker(IPC_NTSETINFO_RENAME_TAG, params.GetBase())) - break; - - InOutCountedBuffer io_status_buffer(io_status, sizeof(IO_STATUS_BLOCK)); - // This is actually not an InOut buffer, only In, but using InOut facility - // really helps to simplify the code. - InOutCountedBuffer file_info_buffer(file_info, length); - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_NTSETINFO_RENAME_TAG, file, - io_status_buffer, file_info_buffer, length, - file_info_class, &answer); - - if (SBOX_ALL_OK != code) - break; - - status = answer.nt_status; - } while (false); - - return status; -} - -} // namespace sandbox diff --git a/sandbox/src/filesystem_interception.h b/sandbox/src/filesystem_interception.h deleted file mode 100644 index d8d9e58..0000000 --- a/sandbox/src/filesystem_interception.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -#ifndef SANDBOX_SRC_FILESYSTEM_INTERCEPTION_H__ -#define SANDBOX_SRC_FILESYSTEM_INTERCEPTION_H__ - -namespace sandbox { - -extern "C" { - -// Interception of NtCreateFile on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateFile( - NtCreateFileFunction orig_CreateFile, PHANDLE file, - ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes, - PIO_STATUS_BLOCK io_status, PLARGE_INTEGER allocation_size, - ULONG file_attributes, ULONG sharing, ULONG disposition, ULONG options, - PVOID ea_buffer, ULONG ea_length); - -// Interception of NtOpenFile on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenFile( - NtOpenFileFunction orig_OpenFile, PHANDLE file, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, - ULONG sharing, ULONG options); - -// Interception of NtQueryAtttributesFile on the child process. -// It should never be called directly. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryAttributesFile( - NtQueryAttributesFileFunction orig_QueryAttributes, - POBJECT_ATTRIBUTES object_attributes, - PFILE_BASIC_INFORMATION file_attributes); - -// Interception of NtQueryFullAtttributesFile on the child process. -// It should never be called directly. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile( - NtQueryFullAttributesFileFunction orig_QueryAttributes, - POBJECT_ATTRIBUTES object_attributes, - PFILE_NETWORK_OPEN_INFORMATION file_attributes); - -// Interception of NtSetInformationFile on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationFile( - NtSetInformationFileFunction orig_SetInformationFile, HANDLE file, - PIO_STATUS_BLOCK io_status, PVOID file_information, ULONG length, - FILE_INFORMATION_CLASS file_information_class); - -} // extern "C" - -} // namespace sandbox - -#endif // SANDBOX_SRC_FILESYSTEM_INTERCEPTION_H__ diff --git a/sandbox/src/filesystem_policy.cc b/sandbox/src/filesystem_policy.cc deleted file mode 100644 index 385f4ae..0000000 --- a/sandbox/src/filesystem_policy.cc +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "sandbox/src/filesystem_policy.h" - -#include "base/logging.h" -#include "base/win/scoped_handle.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_engine_opcodes.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox_utils.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/win_utils.h" - -namespace { - -NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle, - ACCESS_MASK desired_access, - OBJECT_ATTRIBUTES* obj_attributes, - IO_STATUS_BLOCK* io_status_block, - ULONG file_attributes, - ULONG share_access, - ULONG create_disposition, - ULONG create_options, - PVOID ea_buffer, - ULONG ea_lenght, - HANDLE target_process) { - NtCreateFileFunction NtCreateFile = NULL; - ResolveNTFunctionPtr("NtCreateFile", &NtCreateFile); - - HANDLE local_handle = INVALID_HANDLE_VALUE; - NTSTATUS status = NtCreateFile(&local_handle, desired_access, obj_attributes, - io_status_block, NULL, file_attributes, - share_access, create_disposition, - create_options, ea_buffer, ea_lenght); - if (!NT_SUCCESS(status)) { - return status; - } - - if (!sandbox::SameObject(local_handle, obj_attributes->ObjectName->Buffer)) { - // The handle points somewhere else. Fail the operation. - ::CloseHandle(local_handle); - return STATUS_ACCESS_DENIED; - } - - if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - target_process, target_file_handle, 0, FALSE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(local_handle); - return STATUS_ACCESS_DENIED; - } - return STATUS_SUCCESS; -} - -} // namespace. - -namespace sandbox { - -bool FileSystemPolicy::GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy) { - std::wstring mod_name(name); - if (mod_name.empty()) { - return false; - } - - // Don't do any pre-processing if the name starts like the the native - // object manager style. - if (0 != _wcsnicmp(mod_name.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) { - // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the - // infrastructure to normalize names. In any case we need to escape the - // question marks. - if (!PreProcessName(mod_name, &mod_name)) { - // The path to be added might contain a reparse point. - NOTREACHED(); - return false; - } - if (0 != mod_name.compare(0, kNTPrefixLen, kNTPrefix)) { - // TODO(nsylvain): Find a better way to do name resolution. Right now we - // take the name and we expand it. - mod_name.insert(0, L"\\/?/?\\"); - name = mod_name.c_str(); - } - } - - EvalResult result = ASK_BROKER; - - // List of supported calls for the filesystem. - const unsigned kCallNtCreateFile = 0x1; - const unsigned kCallNtOpenFile = 0x2; - const unsigned kCallNtQueryAttributesFile = 0x4; - const unsigned kCallNtQueryFullAttributesFile = 0x8; - const unsigned kCallNtSetInfoRename = 0x10; - - DWORD rule_to_add = kCallNtOpenFile | kCallNtCreateFile | - kCallNtQueryAttributesFile | - kCallNtQueryFullAttributesFile | kCallNtSetInfoRename; - - PolicyRule create(result); - PolicyRule open(result); - PolicyRule query(result); - PolicyRule query_full(result); - PolicyRule rename(result); - - switch (semantics) { - case TargetPolicy::FILES_ALLOW_DIR_ANY: { - open.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND); - create.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND); - break; - } - case TargetPolicy::FILES_ALLOW_READONLY: { - // We consider all flags that are not known to be readonly as potentially - // used for write. - DWORD allowed_flags = FILE_READ_DATA | FILE_READ_ATTRIBUTES | - FILE_READ_EA | SYNCHRONIZE | FILE_EXECUTE | - GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL; - DWORD restricted_flags = ~allowed_flags; - open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); - create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); - - // Read only access don't work for rename. - rule_to_add &= ~kCallNtSetInfoRename; - break; - } - case TargetPolicy::FILES_ALLOW_QUERY: { - // Here we don't want to add policy for the open or the create. - rule_to_add &= ~(kCallNtOpenFile | kCallNtCreateFile | - kCallNtSetInfoRename); - break; - } - case TargetPolicy::FILES_ALLOW_ANY: { - break; - } - default: { - NOTREACHED(); - return false; - } - } - - if ((rule_to_add & kCallNtCreateFile) && - (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) || - !policy->AddRule(IPC_NTCREATEFILE_TAG, &create))) { - return false; - } - - if ((rule_to_add & kCallNtOpenFile) && - (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) || - !policy->AddRule(IPC_NTOPENFILE_TAG, &open))) { - return false; - } - - if ((rule_to_add & kCallNtQueryAttributesFile) && - (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) || - !policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &query))) { - return false; - } - - if ((rule_to_add & kCallNtQueryFullAttributesFile) && - (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) - || !policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, - &query_full))) { - return false; - } - - if ((rule_to_add & kCallNtSetInfoRename) && - (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) || - !policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &rename))) { - return false; - } - - return true; -} - -// Right now we insert two rules, to be evaluated before any user supplied rule: -// - go to the broker if the path doesn't look like the paths that we push on -// the policy (namely \??\something). -// - go to the broker if it looks like this is a short-name path. -// -// It is possible to add a rule to go to the broker in any case; it would look -// something like: -// rule = new PolicyRule(ASK_BROKER); -// rule->AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); -// policy->AddRule(service, rule); -bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) { - PolicyRule format(ASK_BROKER); - PolicyRule short_name(ASK_BROKER); - - bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); - rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*", - CASE_SENSITIVE); - - rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); - rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE); - - if (!rv || !policy->AddRule(IPC_NTCREATEFILE_TAG, &format)) - return false; - - if (!policy->AddRule(IPC_NTCREATEFILE_TAG, &short_name)) - return false; - - if (!policy->AddRule(IPC_NTOPENFILE_TAG, &format)) - return false; - - if (!policy->AddRule(IPC_NTOPENFILE_TAG, &short_name)) - return false; - - if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &format)) - return false; - - if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &short_name)) - return false; - - if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &format)) - return false; - - if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &short_name)) - return false; - - if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &format)) - return false; - - if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &short_name)) - return false; - - return true; -} - -bool FileSystemPolicy::CreateFileAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &file, - uint32 attributes, - uint32 desired_access, - uint32 file_attributes, - uint32 share_access, - uint32 create_disposition, - uint32 create_options, - HANDLE *handle, - NTSTATUS* nt_status, - ULONG_PTR *io_information) { - // The only action supported is ASK_BROKER which means create the requested - // file as specified. - if (ASK_BROKER != eval_result) { - *nt_status = STATUS_ACCESS_DENIED; - return false; - } - IO_STATUS_BLOCK io_block = {0}; - UNICODE_STRING uni_name = {0}; - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); - *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes, - &io_block, file_attributes, share_access, - create_disposition, create_options, NULL, - 0, client_info.process); - - *io_information = io_block.Information; - return true; -} - -bool FileSystemPolicy::OpenFileAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &file, - uint32 attributes, - uint32 desired_access, - uint32 share_access, - uint32 open_options, - HANDLE *handle, - NTSTATUS* nt_status, - ULONG_PTR *io_information) { - // The only action supported is ASK_BROKER which means open the requested - // file as specified. - if (ASK_BROKER != eval_result) { - *nt_status = STATUS_ACCESS_DENIED; - return true; - } - // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and - // CreateDisposition = FILE_OPEN. - IO_STATUS_BLOCK io_block = {0}; - UNICODE_STRING uni_name = {0}; - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); - *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes, - &io_block, 0, share_access, FILE_OPEN, - open_options, NULL, 0, - client_info.process); - - *io_information = io_block.Information; - return true; -} - -bool FileSystemPolicy::QueryAttributesFileAction( - EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &file, - uint32 attributes, - FILE_BASIC_INFORMATION* file_info, - NTSTATUS* nt_status) { - // The only action supported is ASK_BROKER which means query the requested - // file as specified. - if (ASK_BROKER != eval_result) { - *nt_status = STATUS_ACCESS_DENIED; - return true; - } - - NtQueryAttributesFileFunction NtQueryAttributesFile = NULL; - ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile); - - UNICODE_STRING uni_name = {0}; - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); - *nt_status = NtQueryAttributesFile(&obj_attributes, file_info); - - return true; -} - -bool FileSystemPolicy::QueryFullAttributesFileAction( - EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &file, - uint32 attributes, - FILE_NETWORK_OPEN_INFORMATION* file_info, - NTSTATUS* nt_status) { - // The only action supported is ASK_BROKER which means query the requested - // file as specified. - if (ASK_BROKER != eval_result) { - *nt_status = STATUS_ACCESS_DENIED; - return true; - } - - NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = NULL; - ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile); - - UNICODE_STRING uni_name = {0}; - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); - *nt_status = NtQueryFullAttributesFile(&obj_attributes, file_info); - - return true; -} - -bool FileSystemPolicy::SetInformationFileAction( - EvalResult eval_result, const ClientInfo& client_info, - HANDLE target_file_handle, void* file_info, uint32 length, - uint32 info_class, IO_STATUS_BLOCK* io_block, - NTSTATUS* nt_status) { - // The only action supported is ASK_BROKER which means open the requested - // file as specified. - if (ASK_BROKER != eval_result) { - *nt_status = STATUS_ACCESS_DENIED; - return true; - } - - NtSetInformationFileFunction NtSetInformationFile = NULL; - ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile); - - HANDLE local_handle = NULL; - if (!::DuplicateHandle(client_info.process, target_file_handle, - ::GetCurrentProcess(), &local_handle, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - *nt_status = STATUS_ACCESS_DENIED; - return true; - } - - base::win::ScopedHandle handle(local_handle); - - FILE_INFORMATION_CLASS file_info_class = - static_cast(info_class); - *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length, - file_info_class); - - return true; -} - -bool PreProcessName(const std::wstring& path, std::wstring* new_path) { - ConvertToLongPath(path, new_path); - - bool reparsed = false; - if (ERROR_SUCCESS != IsReparsePoint(*new_path, &reparsed)) - return false; - - // We can't process reparsed file. - return !reparsed; -} - -} // namespace sandbox diff --git a/sandbox/src/filesystem_policy.h b/sandbox/src/filesystem_policy.h deleted file mode 100644 index 5010a9f..0000000 --- a/sandbox/src/filesystem_policy.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_FILESYSTEM_POLICY_H__ -#define SANDBOX_SRC_FILESYSTEM_POLICY_H__ - -#include - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/policy_low_level.h" -#include "sandbox/src/sandbox_policy.h" - -namespace sandbox { - -enum EvalResult; - -// This class centralizes most of the knowledge related to file system policy -class FileSystemPolicy { - public: - // Creates the required low-level policy rules to evaluate a high-level - // policy rule for File IO, in particular open or create actions. - // 'name' is the file or directory name. - // 'semantics' is the desired semantics for the open or create. - // 'policy' is the policy generator to which the rules are going to be added. - static bool GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy); - - // Add basic file system rules. - static bool SetInitialRules(LowLevelPolicy* policy); - - // Performs the desired policy action on a create request with an - // API that is compatible with the IPC-received parameters. - // 'client_info' : the target process that is making the request. - // 'eval_result' : The desired policy action to accomplish. - // 'file' : The target file or directory. - static bool CreateFileAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &file, - uint32 attributes, - uint32 desired_access, - uint32 file_attributes, - uint32 share_access, - uint32 create_disposition, - uint32 create_options, - HANDLE* handle, - NTSTATUS* nt_status, - ULONG_PTR* io_information); - - // Performs the desired policy action on an open request with an - // API that is compatible with the IPC-received parameters. - // 'client_info' : the target process that is making the request. - // 'eval_result' : The desired policy action to accomplish. - // 'file' : The target file or directory. - static bool OpenFileAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &file, - uint32 attributes, - uint32 desired_access, - uint32 share_access, - uint32 open_options, - HANDLE* handle, - NTSTATUS* nt_status, - ULONG_PTR* io_information); - - // Performs the desired policy action on a query request with an - // API that is compatible with the IPC-received parameters. - static bool QueryAttributesFileAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &file, - uint32 attributes, - FILE_BASIC_INFORMATION* file_info, - NTSTATUS* nt_status); - - // Performs the desired policy action on a query request with an - // API that is compatible with the IPC-received parameters. - static bool QueryFullAttributesFileAction( - EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &file, - uint32 attributes, - FILE_NETWORK_OPEN_INFORMATION* file_info, - NTSTATUS* nt_status); - - // Performs the desired policy action on a set_info request with an - // API that is compatible with the IPC-received parameters. - static bool SetInformationFileAction(EvalResult eval_result, - const ClientInfo& client_info, - HANDLE target_file_handle, - void* file_info, - uint32 length, - uint32 info_class, - IO_STATUS_BLOCK* io_block, - NTSTATUS* nt_status); -}; - -// Expands the path and check if it's a reparse point. Returns false if -// we cannot determine or if there is an unexpected error. In that case -// the path cannot be trusted. -bool PreProcessName(const std::wstring& path, std::wstring* new_path); - -} // namespace sandbox - -#endif // SANDBOX_SRC_FILESYSTEM_POLICY_H__ diff --git a/sandbox/src/handle_closer.cc b/sandbox/src/handle_closer.cc deleted file mode 100644 index d79f2c1..0000000 --- a/sandbox/src/handle_closer.cc +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/handle_closer.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/win/windows_version.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/internal_types.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/process_thread_interception.h" -#include "sandbox/src/win_utils.h" - -namespace { - -template T RoundUpToWordSize(T v) { - if (size_t mod = v % sizeof(size_t)) - v += sizeof(size_t) - mod; - return v; -} - -template T* RoundUpToWordSize(T* v) { - return reinterpret_cast(RoundUpToWordSize(reinterpret_cast(v))); -} - -} // namespace - -namespace sandbox { - -// Memory buffer mapped from the parent, with the list of handles. -SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close; - -HandleCloser::HandleCloser() {} - -ResultCode HandleCloser::AddHandle(const char16* handle_type, - const char16* handle_name) { - if (!handle_type) - return SBOX_ERROR_BAD_PARAMS; - - HandleMap::iterator names = handles_to_close_.find(handle_type); - if (names == handles_to_close_.end()) { // We have no entries for this type. - std::pair result = handles_to_close_.insert( - HandleMap::value_type(handle_type, HandleMap::mapped_type())); - names = result.first; - if (handle_name) - names->second.insert(handle_name); - } else if (!handle_name) { // Now we need to close all handles of this type. - names->second.clear(); - } else if (!names->second.empty()) { // Add another name for this type. - names->second.insert(handle_name); - } // If we're already closing all handles of type then we're done. - - return SBOX_ALL_OK; -} - -size_t HandleCloser::GetBufferSize() { - size_t bytes_total = offsetof(HandleCloserInfo, handle_entries); - - for (HandleMap::iterator i = handles_to_close_.begin(); - i != handles_to_close_.end(); ++i) { - size_t bytes_entry = offsetof(HandleListEntry, handle_type) + - (i->first.size() + 1) * sizeof(char16); - for (HandleMap::mapped_type::iterator j = i->second.begin(); - j != i->second.end(); ++j) { - bytes_entry += ((*j).size() + 1) * sizeof(char16); - } - - // Round up to the nearest multiple of word size. - bytes_entry = RoundUpToWordSize(bytes_entry); - bytes_total += bytes_entry; - } - - return bytes_total; -} - -bool HandleCloser::InitializeTargetHandles(TargetProcess* target) { - // Do nothing on an empty list (global pointer already initialized to NULL). - if (handles_to_close_.empty()) - return true; - - size_t bytes_needed = GetBufferSize(); - scoped_array local_buffer( - new size_t[bytes_needed / sizeof(size_t)]); - - if (!SetupHandleList(local_buffer.get(), bytes_needed)) - return false; - - HANDLE child = target->Process(); - - // Allocate memory in the target process without specifying the address - void* remote_data = ::VirtualAllocEx(child, NULL, bytes_needed, - MEM_COMMIT, PAGE_READWRITE); - if (NULL == remote_data) - return false; - - // Copy the handle buffer over. - SIZE_T bytes_written; - BOOL result = ::WriteProcessMemory(child, remote_data, local_buffer.get(), - bytes_needed, &bytes_written); - if (!result || bytes_written != bytes_needed) { - ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE); - return false; - } - - g_handles_to_close = reinterpret_cast(remote_data); - - ResultCode rc = target->TransferVariable("g_handles_to_close", - &g_handles_to_close, - sizeof(g_handles_to_close)); - - return (SBOX_ALL_OK == rc); -} - -bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) { - ::ZeroMemory(buffer, buffer_bytes); - HandleCloserInfo* handle_info = reinterpret_cast(buffer); - handle_info->record_bytes = buffer_bytes; - handle_info->num_handle_types = handles_to_close_.size(); - - char16* output = reinterpret_cast(&handle_info->handle_entries[0]); - char16* end = reinterpret_cast( - reinterpret_cast(buffer) + buffer_bytes); - for (HandleMap::iterator i = handles_to_close_.begin(); - i != handles_to_close_.end(); ++i) { - if (output >= end) - return false; - HandleListEntry* list_entry = reinterpret_cast(output); - output = &list_entry->handle_type[0]; - - // Copy the typename and set the offset and count. - i->first._Copy_s(output, i->first.size(), i->first.size()); - *(output += i->first.size()) = L'\0'; - output++; - list_entry->offset_to_names = reinterpret_cast(output) - - reinterpret_cast(list_entry); - list_entry->name_count = i->second.size(); - - // Copy the handle names. - for (HandleMap::mapped_type::iterator j = i->second.begin(); - j != i->second.end(); ++j) { - output = std::copy((*j).begin(), (*j).end(), output) + 1; - } - - // Round up to the nearest multiple of sizeof(size_t). - output = RoundUpToWordSize(output); - list_entry->record_bytes = reinterpret_cast(output) - - reinterpret_cast(list_entry); - } - - DCHECK_EQ(reinterpret_cast(output), reinterpret_cast(end)); - return output <= end; -} - -bool HandleCloser::SetupHandleInterceptions(InterceptionManager* manager) { - // We need to intercept CreateThread if we're closing ALPC port clients. - HandleMap::iterator names = handles_to_close_.find(L"ALPC Port"); - if (base::win::GetVersion() >= base::win::VERSION_VISTA && - names != handles_to_close_.end() && - (names->second.empty() || names->second.size() == 0)) { - if (!INTERCEPT_EAT(manager, kKerneldllName, CreateThread, - CREATE_THREAD_ID, 28)) { - return false; - } - if (!INTERCEPT_EAT(manager, kKerneldllName, GetUserDefaultLCID, - GET_USER_DEFAULT_LCID_ID, 4)) { - return false; - } - - return true; - } - - return true; -} - -bool GetHandleName(HANDLE handle, string16* handle_name) { - static NtQueryObject QueryObject = NULL; - if (!QueryObject) - ResolveNTFunctionPtr("NtQueryObject", &QueryObject); - - ULONG size = MAX_PATH; - scoped_ptr name; - NTSTATUS result; - - do { - name.reset(reinterpret_cast(new BYTE[size])); - result = QueryObject(handle, ObjectNameInformation, name.get(), - size, &size); - } while (result == STATUS_INFO_LENGTH_MISMATCH || - result == STATUS_BUFFER_OVERFLOW); - - if (NT_SUCCESS(result) && name->Buffer && name->Length) - handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t)); - else - handle_name->clear(); - - return NT_SUCCESS(result); -} - -} // namespace sandbox diff --git a/sandbox/src/handle_closer.h b/sandbox/src/handle_closer.h deleted file mode 100644 index f680169..0000000 --- a/sandbox/src/handle_closer.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_HANDLE_CLOSER_H_ -#define SANDBOX_SRC_HANDLE_CLOSER_H_ - -#include -#include - -#include "base/basictypes.h" -#include "base/string16.h" -#include "sandbox/src/interception.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/target_process.h" - -namespace sandbox { - -// This is a map of handle-types to names that we need to close in the -// target process. A null set means we need to close all handles of the -// given type. -typedef std::map > HandleMap; - -// Type and set of corresponding handle names to close. -struct HandleListEntry { - size_t record_bytes; // Rounded to sizeof(size_t) bytes. - size_t offset_to_names; // Nul terminated strings of name_count names. - size_t name_count; - char16 handle_type[1]; -}; - -// Global parameters and a pointer to the list of entries. -struct HandleCloserInfo { - size_t record_bytes; // Rounded to sizeof(size_t) bytes. - size_t num_handle_types; - struct HandleListEntry handle_entries[1]; -}; - -SANDBOX_INTERCEPT HandleCloserInfo* g_handle_closer_info; - -// Adds handles to close after lockdown. -class HandleCloser { - public: - HandleCloser(); - - // Adds a handle that will be closed in the target process after lockdown. - // A NULL value for handle_name indicates all handles of the specified type. - // An empty string for handle_name indicates the handle is unnamed. - ResultCode AddHandle(const char16* handle_type, const char16* handle_name); - - // Serializes and copies the closer table into the target process. - bool InitializeTargetHandles(TargetProcess* target); - - // Adds any interceptions that may be required due to closed system handles. - bool SetupHandleInterceptions(InterceptionManager* manager); - - private: - // Calculates the memory needed to copy the serialized handles list (rounded - // to the nearest machine-word size). - size_t GetBufferSize(); - - // Serializes the handle list into the target process. - bool SetupHandleList(void* buffer, size_t buffer_bytes); - - HandleMap handles_to_close_; - - DISALLOW_COPY_AND_ASSIGN(HandleCloser); -}; - -// Returns the object manager's name associated with a handle -bool GetHandleName(HANDLE handle, string16* handle_name); - -} // namespace sandbox - -#endif // SANDBOX_SRC_HANDLE_CLOSER_H_ diff --git a/sandbox/src/handle_closer_agent.cc b/sandbox/src/handle_closer_agent.cc deleted file mode 100644 index 2b5ac97..0000000 --- a/sandbox/src/handle_closer_agent.cc +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/handle_closer_agent.h" - -#include "base/logging.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/win_utils.h" - -namespace { - -// Returns type infomation for an NT object. This routine is expected to be -// called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions -// that can be generated when handle tracing is enabled. -NTSTATUS QueryObjectTypeInformation(HANDLE handle, - void* buffer, - ULONG* size) { - static NtQueryObject QueryObject = NULL; - if (!QueryObject) - ResolveNTFunctionPtr("NtQueryObject", &QueryObject); - - NTSTATUS status = STATUS_UNSUCCESSFUL; - __try { - status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size); - } __except(GetExceptionCode() == STATUS_INVALID_HANDLE ? - EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { - status = STATUS_INVALID_HANDLE; - } - return status; -} - -} // namespace - -namespace sandbox { - -// Memory buffer mapped from the parent, with the list of handles. -SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close = NULL; - -bool HandleCloserAgent::NeedsHandlesClosed() { - return g_handles_to_close != NULL; -} - -// Reads g_handles_to_close and creates the lookup map. -void HandleCloserAgent::InitializeHandlesToClose() { - CHECK(g_handles_to_close != NULL); - - // Grab the header. - HandleListEntry* entry = g_handles_to_close->handle_entries; - for (size_t i = 0; i < g_handles_to_close->num_handle_types; ++i) { - // Set the type name. - char16* input = entry->handle_type; - HandleMap::mapped_type& handle_names = handles_to_close_[input]; - input = reinterpret_cast(reinterpret_cast(entry) - + entry->offset_to_names); - // Grab all the handle names. - for (size_t j = 0; j < entry->name_count; ++j) { - std::pair name - = handle_names.insert(input); - CHECK(name.second); - input += name.first->size() + 1; - } - - // Move on to the next entry. - entry = reinterpret_cast(reinterpret_cast(entry) - + entry->record_bytes); - - DCHECK(reinterpret_cast(entry) >= input); - DCHECK(reinterpret_cast(entry) - input < - sizeof(size_t) / sizeof(char16)); - } - - // Clean up the memory we copied over. - ::VirtualFree(g_handles_to_close, 0, MEM_RELEASE); - g_handles_to_close = NULL; -} - -bool HandleCloserAgent::CloseHandles() { - DWORD handle_count = UINT_MAX; - const int kInvalidHandleThreshold = 100; - const size_t kHandleOffset = sizeof(HANDLE); - - if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count)) - return false; - - // Set up buffers for the type info and the name. - std::vector type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) + - 32 * sizeof(wchar_t)); - OBJECT_TYPE_INFORMATION* type_info = - reinterpret_cast(&(type_info_buffer[0])); - string16 handle_name; - HANDLE handle = NULL; - int invalid_count = 0; - - // Keep incrementing until we hit the number of handles reported by - // GetProcessHandleCount(). If we hit a very long sequence of invalid - // handles we assume that we've run past the end of the table. - while (handle_count && invalid_count < kInvalidHandleThreshold) { - reinterpret_cast(handle) += kHandleOffset; - NTSTATUS rc; - - // Get the type name, reusing the buffer. - ULONG size = static_cast(type_info_buffer.size()); - rc = QueryObjectTypeInformation(handle, type_info, &size); - while (rc == STATUS_INFO_LENGTH_MISMATCH || - rc == STATUS_BUFFER_OVERFLOW) { - type_info_buffer.resize(size + sizeof(wchar_t)); - type_info = reinterpret_cast( - &(type_info_buffer[0])); - rc = QueryObjectTypeInformation(handle, type_info, &size); - // Leave padding for the nul terminator. - if (NT_SUCCESS(0) && size == type_info_buffer.size()) - rc = STATUS_INFO_LENGTH_MISMATCH; - } - if (!NT_SUCCESS(rc) || !type_info->Name.Buffer) { - ++invalid_count; - continue; - } - - --handle_count; - type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0'; - - // Check if we're looking for this type of handle. - HandleMap::iterator result = - handles_to_close_.find(type_info->Name.Buffer); - if (result != handles_to_close_.end()) { - HandleMap::mapped_type& names = result->second; - // Empty set means close all handles of this type; otherwise check name. - if (!names.empty()) { - // Move on to the next handle if this name doesn't match. - if (!GetHandleName(handle, &handle_name) || !names.count(handle_name)) - continue; - } - - if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0)) - return false; - if (!::CloseHandle(handle)) - return false; - } - } - - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/handle_closer_agent.h b/sandbox/src/handle_closer_agent.h deleted file mode 100644 index c74987c..0000000 --- a/sandbox/src/handle_closer_agent.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_ -#define SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_ - -#include "base/basictypes.h" -#include "base/string16.h" -#include "sandbox/src/handle_closer.h" -#include "sandbox/src/sandbox_types.h" - -namespace sandbox { - -// Target process code to close the handle list copied over from the broker. -class HandleCloserAgent { - public: - HandleCloserAgent() {} - - // Reads the serialized list from the broker and creates the lookup map. - void InitializeHandlesToClose(); - - // Closes any handles matching those in the lookup map. - bool CloseHandles(); - - // True if we have handles waiting to be closed - static bool NeedsHandlesClosed(); - - private: - HandleMap handles_to_close_; - - DISALLOW_COPY_AND_ASSIGN(HandleCloserAgent); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_ diff --git a/sandbox/src/handle_closer_test.cc b/sandbox/src/handle_closer_test.cc deleted file mode 100644 index 81b6db2..0000000 --- a/sandbox/src/handle_closer_test.cc +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/stringprintf.h" -#include "base/win/scoped_handle.h" -#include "sandbox/src/handle_closer_agent.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/target_services.h" -#include "sandbox/tests/common/controller.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -const wchar_t *kFileExtensions[] = { L".1", L".2", L".3", L".4" }; - -// Returns a handle to a unique marker file that can be retrieved between runs. -HANDLE GetMarkerFile(const wchar_t *extension) { - wchar_t path_buffer[MAX_PATH + 1]; - CHECK(::GetTempPath(MAX_PATH, path_buffer)); - string16 marker_path = path_buffer; - marker_path += L"\\sbox_marker_"; - - // Generate a unique value from the exe's size and timestamp. - CHECK(::GetModuleFileName(NULL, path_buffer, MAX_PATH)); - base::win::ScopedHandle module(::CreateFile(path_buffer, - FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); - CHECK(module.IsValid()); - FILETIME timestamp; - CHECK(::GetFileTime(module, ×tamp, NULL, NULL)); - marker_path += base::StringPrintf(L"%08x%08x%08x", - ::GetFileSize(module, NULL), - timestamp.dwLowDateTime, - timestamp.dwHighDateTime); - marker_path += extension; - - // Make the file delete-on-close so cleanup is automatic. - return CreateFile(marker_path.c_str(), FILE_ALL_ACCESS, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL); -} - -// Used by the thread pool tests. -HANDLE finish_event; -const int kWaitCount = 20; - -} // namespace - -namespace sandbox { - -// Checks for the presence of a list of files (in object path form). -// Format: CheckForFileHandle (Y|N) \path\to\file1 [\path\to\file2 ...] -// - Y or N depending if the file should exist or not. -SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t **argv) { - if (argc < 2) - return SBOX_TEST_FAILED_TO_RUN_TEST; - bool should_find = argv[0][0] == L'Y'; - if (argv[0][1] != L'\0' || !should_find && argv[0][0] != L'N') - return SBOX_TEST_FAILED_TO_RUN_TEST; - - static int state = BEFORE_INIT; - switch (state++) { - case BEFORE_INIT: - // Create a unique marker file that is open while the test is running. - // The handles leak, but it will be closed by the test or on exit. - for (int i = 0; i < arraysize(kFileExtensions); ++i) - EXPECT_NE(GetMarkerFile(kFileExtensions[i]), INVALID_HANDLE_VALUE); - return SBOX_TEST_SUCCEEDED; - - case AFTER_REVERT: { - // Brute force the handle table to find what we're looking for. - DWORD handle_count = UINT_MAX; - const int kInvalidHandleThreshold = 100; - const size_t kHandleOffset = sizeof(HANDLE); - HANDLE handle = NULL; - int invalid_count = 0; - string16 handle_name; - - if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count)) - return SBOX_TEST_FAILED_TO_RUN_TEST; - - while (handle_count && invalid_count < kInvalidHandleThreshold) { - reinterpret_cast(handle) += kHandleOffset; - if (GetHandleName(handle, &handle_name)) { - for (int i = 1; i < argc; ++i) { - if (handle_name == argv[i]) - return should_find ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; - } - --handle_count; - } else { - ++invalid_count; - } - } - - return should_find ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED; - } - - default: // Do nothing. - break; - } - - return SBOX_TEST_SUCCEEDED; -} - -TEST(HandleCloserTest, CheckForMarkerFiles) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(EVERY_STATE); - sandbox::TargetPolicy* policy = runner.GetPolicy(); - - string16 command = string16(L"CheckForFileHandles Y"); - for (int i = 0; i < arraysize(kFileExtensions); ++i) { - string16 handle_name; - base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i])); - CHECK(marker.IsValid()); - CHECK(sandbox::GetHandleName(marker, &handle_name)); - command += (L" "); - command += handle_name; - } - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) << - "Failed: " << command; -} - -TEST(HandleCloserTest, CloseMarkerFiles) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(EVERY_STATE); - sandbox::TargetPolicy* policy = runner.GetPolicy(); - - string16 command = string16(L"CheckForFileHandles N"); - for (int i = 0; i < arraysize(kFileExtensions); ++i) { - string16 handle_name; - base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i])); - CHECK(marker.IsValid()); - CHECK(sandbox::GetHandleName(marker, &handle_name)); - CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()), - SBOX_ALL_OK); - command += (L" "); - command += handle_name; - } - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) << - "Failed: " << command; -} - -void WINAPI ThreadPoolTask(void* event, BOOLEAN timeout) { - static volatile LONG waiters_remaining = kWaitCount; - CHECK(!timeout); - CHECK(::CloseHandle(event)); - if (::InterlockedDecrement(&waiters_remaining) == 0) - CHECK(::SetEvent(finish_event)); -} - -// Run a thread pool inside a sandbox without a CSRSS connection. -SBOX_TESTS_COMMAND int RunThreadPool(int argc, wchar_t **argv) { - HANDLE wait_list[20]; - CHECK(finish_event = ::CreateEvent(NULL, TRUE, FALSE, NULL)); - - // Set up a bunch of waiters. - HANDLE pool = NULL; - for (int i = 0; i < kWaitCount; ++i) { - HANDLE event = ::CreateEvent(NULL, TRUE, FALSE, NULL); - CHECK(event); - CHECK(::RegisterWaitForSingleObject(&pool, event, ThreadPoolTask, event, - INFINITE, WT_EXECUTEONLYONCE)); - wait_list[i] = event; - } - - // Signal all the waiters. - for (int i = 0; i < kWaitCount; ++i) - CHECK(::SetEvent(wait_list[i])); - - CHECK_EQ(::WaitForSingleObject(finish_event, INFINITE), WAIT_OBJECT_0); - CHECK(::CloseHandle(finish_event)); - - return SBOX_TEST_SUCCEEDED; -} - -TEST(HandleCloserTest, RunThreadPool) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(AFTER_REVERT); - sandbox::TargetPolicy* policy = runner.GetPolicy(); - - // Sever the CSRSS connection by closing ALPC ports inside the sandbox. - CHECK_EQ(policy->AddKernelObjectToClose(L"ALPC Port", NULL), SBOX_ALL_OK); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"RunThreadPool")); -} - -} // namespace sandbox diff --git a/sandbox/src/handle_dispatcher.cc b/sandbox/src/handle_dispatcher.cc deleted file mode 100644 index 7a18cee..0000000 --- a/sandbox/src/handle_dispatcher.cc +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/handle_dispatcher.h" - -#include "base/win/scoped_handle.h" -#include "sandbox/src/handle_interception.h" -#include "sandbox/src/handle_policy.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_broker.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/sandbox_utils.h" - -namespace sandbox { - -HandleDispatcher::HandleDispatcher(PolicyBase* policy_base) - : policy_base_(policy_base) { - static const IPCCall duplicate_handle_proxy = { - {IPC_DUPLICATEHANDLEPROXY_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE, - ULONG_TYPE}, - reinterpret_cast(&HandleDispatcher::DuplicateHandleProxy) - }; - - ipc_calls_.push_back(duplicate_handle_proxy); -} - -bool HandleDispatcher::SetupService(InterceptionManager* manager, - int service) { - // We perform no interceptions for handles right now. - switch (service) { - case IPC_DUPLICATEHANDLEPROXY_TAG: - return true; - } - - return false; -} - -bool HandleDispatcher::DuplicateHandleProxy(IPCInfo* ipc, - HANDLE source_handle, - DWORD target_process_id, - DWORD desired_access, - DWORD options) { - NTSTATUS error; - static NtQueryObject QueryObject = NULL; - if (!QueryObject) - ResolveNTFunctionPtr("NtQueryObject", &QueryObject); - - // Get a copy of the handle for use in the broker process. - HANDLE handle_temp; - if (!::DuplicateHandle(ipc->client_info->process, source_handle, - ::GetCurrentProcess(), &handle_temp, - 0, FALSE, DUPLICATE_SAME_ACCESS)) { - ipc->return_info.win32_result = ::GetLastError(); - return false; - } - base::win::ScopedHandle handle(handle_temp); - - // Get the object type (32 characters is safe; current max is 14). - BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)]; - OBJECT_TYPE_INFORMATION* type_info = - reinterpret_cast(buffer); - ULONG size = sizeof(buffer) - sizeof(wchar_t); - error = QueryObject(handle, ObjectTypeInformation, type_info, size, &size); - if (!NT_SUCCESS(error)) { - ipc->return_info.win32_result = error; - return false; - } - type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0'; - - CountedParameterSet params; - params[HandleTarget::NAME] = ParamPickerMake(type_info->Name.Buffer); - params[HandleTarget::TARGET] = ParamPickerMake(target_process_id); - - EvalResult eval = policy_base_->EvalPolicy(IPC_DUPLICATEHANDLEPROXY_TAG, - params.GetBase()); - ipc->return_info.win32_result = - HandlePolicy::DuplicateHandleProxyAction(eval, *ipc->client_info, - source_handle, - target_process_id, - &ipc->return_info.handle, - desired_access, options); - return true; -} - -} // namespace sandbox - diff --git a/sandbox/src/handle_dispatcher.h b/sandbox/src/handle_dispatcher.h deleted file mode 100644 index c1abc28..0000000 --- a/sandbox/src/handle_dispatcher.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_HANDLE_DISPATCHER_H_ -#define SANDBOX_SRC_HANDLE_DISPATCHER_H_ - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sandbox_policy_base.h" - -namespace sandbox { - -// This class handles handle-related IPC calls. -class HandleDispatcher : public Dispatcher { - public: - explicit HandleDispatcher(PolicyBase* policy_base); - ~HandleDispatcher() {} - - // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); - - private: - // Processes IPC requests coming from calls to - // TargetServices::DuplicateHandle() in the target. - bool DuplicateHandleProxy(IPCInfo* ipc, HANDLE source_handle, - DWORD target_process_id, DWORD desired_access, - DWORD options); - - PolicyBase* policy_base_; - DISALLOW_COPY_AND_ASSIGN(HandleDispatcher); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_HANDLE_DISPATCHER_H_ - diff --git a/sandbox/src/handle_interception.cc b/sandbox/src/handle_interception.cc deleted file mode 100644 index 0f7b9f8..0000000 --- a/sandbox/src/handle_interception.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/handle_interception.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/target_services.h" - -namespace sandbox { - -ResultCode DuplicateHandleProxy(HANDLE source_handle, - DWORD target_process_id, - HANDLE* target_handle, - DWORD desired_access, - DWORD options) { - *target_handle = NULL; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - return SBOX_ERROR_NO_SPACE; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_DUPLICATEHANDLEPROXY_TAG, - source_handle, target_process_id, - desired_access, options, &answer); - if (SBOX_ALL_OK != code) - return code; - - if (answer.win32_result) { - ::SetLastError(answer.nt_status); - return SBOX_ERROR_GENERIC; - } - - *target_handle = answer.handle; - return SBOX_ALL_OK; -} - -} // namespace sandbox - diff --git a/sandbox/src/handle_interception.h b/sandbox/src/handle_interception.h deleted file mode 100644 index 543c7ba..0000000 --- a/sandbox/src/handle_interception.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -#ifndef SANDBOX_SRC_HANDLE_INTERCEPTION_H_ -#define SANDBOX_SRC_HANDLE_INTERCEPTION_H_ - -namespace sandbox { - -// TODO(jschuh) Add an interception to catch dangerous DuplicateHandle calls. - -ResultCode DuplicateHandleProxy(HANDLE source_handle, - DWORD target_process_id, - HANDLE* target_handle, - DWORD desired_access, - DWORD options); - -} // namespace sandbox - -#endif // SANDBOX_SRC_HANDLE_INTERCEPTION_H_ - diff --git a/sandbox/src/handle_policy.cc b/sandbox/src/handle_policy.cc deleted file mode 100644 index 355dda8..0000000 --- a/sandbox/src/handle_policy.cc +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/handle_policy.h" - -#include - -#include "base/win/scoped_handle.h" -#include "sandbox/src/broker_services.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_engine_opcodes.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/sandbox_utils.h" - -namespace sandbox { - -bool HandlePolicy::GenerateRules(const wchar_t* type_name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy) { - PolicyRule duplicate_rule(ASK_BROKER); - - switch (semantics) { - case TargetPolicy::HANDLES_DUP_ANY: { - if (!duplicate_rule.AddNumberMatch(IF_NOT, HandleTarget::TARGET, - ::GetCurrentProcessId(), EQUAL)) { - return false; - } - break; - } - - case TargetPolicy::HANDLES_DUP_BROKER: { - if (!duplicate_rule.AddNumberMatch(IF, HandleTarget::TARGET, - ::GetCurrentProcessId(), EQUAL)) { - return false; - } - break; - } - - default: - return false; - } - if (!duplicate_rule.AddStringMatch(IF, HandleTarget::NAME, type_name, - CASE_INSENSITIVE)) { - return false; - } - if (!policy->AddRule(IPC_DUPLICATEHANDLEPROXY_TAG, &duplicate_rule)) { - return false; - } - return true; -} - -DWORD HandlePolicy::DuplicateHandleProxyAction(EvalResult eval_result, - const ClientInfo& client_info, - HANDLE source_handle, - DWORD target_process_id, - HANDLE* target_handle, - DWORD desired_access, - DWORD options) { - // The only action supported is ASK_BROKER which means duplicate the handle. - if (ASK_BROKER != eval_result) { - return ERROR_ACCESS_DENIED; - } - - base::win::ScopedHandle remote_target_process; - if (target_process_id != ::GetCurrentProcessId()) { - // Sandboxed children are dynamic, so we check that manually. - if (!BrokerServicesBase::GetInstance()->IsActiveTarget(target_process_id)) { - return ERROR_ACCESS_DENIED; - } - - remote_target_process.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, - target_process_id)); - if (!remote_target_process.IsValid()) - return ::GetLastError(); - } - - // If the policy didn't block us and we have no valid target, then the broker - // (this process) is the valid target. - HANDLE target_process = remote_target_process.IsValid() ? - remote_target_process : ::GetCurrentProcess(); - DWORD result = ERROR_SUCCESS; - if (!::DuplicateHandle(client_info.process, source_handle, target_process, - target_handle, desired_access, FALSE, - options)) { - return ::GetLastError(); - } - - return ERROR_SUCCESS; -} - -} // namespace sandbox - diff --git a/sandbox/src/handle_policy.h b/sandbox/src/handle_policy.h deleted file mode 100644 index c3b7156..0000000 --- a/sandbox/src/handle_policy.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_HANDLE_POLICY_H_ -#define SANDBOX_SRC_HANDLE_POLICY_H_ - -#include - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/policy_low_level.h" -#include "sandbox/src/sandbox_policy.h" - -namespace sandbox { - -enum EvalResult; - -// This class centralizes most of the knowledge related to handle policy. -class HandlePolicy { - public: - // Creates the required low-level policy rules to evaluate a high-level - // policy rule for handles, in particular duplicate action. - static bool GenerateRules(const wchar_t* type_name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy); - - // Processes a 'TargetPolicy::DuplicateHandle()' request from the target. - static DWORD DuplicateHandleProxyAction(EvalResult eval_result, - const ClientInfo& client_info, - HANDLE source_handle, - DWORD target_process_id, - HANDLE* target_handle, - DWORD desired_access, - DWORD options); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_HANDLE_POLICY_H_ - diff --git a/sandbox/src/handle_policy_test.cc b/sandbox/src/handle_policy_test.cc deleted file mode 100644 index 05eb39b..0000000 --- a/sandbox/src/handle_policy_test.cc +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/stringprintf.h" -#include "sandbox/src/handle_policy.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/win_utils.h" -#include "sandbox/tests/common/controller.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -// Just waits for the supplied number of milliseconds. -SBOX_TESTS_COMMAND int Handle_WaitProcess(int argc, wchar_t **argv) { - if (argc != 1) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - ::Sleep(::wcstoul(argv[0], NULL, 10)); - return SBOX_TEST_TIMED_OUT; -} - -// Attempts to duplicate an event handle into the target process. -SBOX_TESTS_COMMAND int Handle_DuplicateEvent(int argc, wchar_t **argv) { - if (argc != 1) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - // Create a test event to use as a handle. - base::win::ScopedHandle test_event; - test_event.Set(::CreateEvent(NULL, TRUE, TRUE, NULL)); - if (!test_event.IsValid()) - return SBOX_TEST_FIRST_ERROR; - - // Get the target process ID. - DWORD target_process_id = ::wcstoul(argv[0], NULL, 10); - - HANDLE handle = NULL; - ResultCode result = SandboxFactory::GetTargetServices()->DuplicateHandle( - test_event, target_process_id, &handle, 0, DUPLICATE_SAME_ACCESS); - - return (result == SBOX_ALL_OK) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED; -} - -// Tests that duplicating an object works only when the policy allows it. -TEST(HandlePolicyTest, DuplicateHandle) { - TestRunner target; - TestRunner runner; - - // Kick off an asynchronous target process for testing. - target.SetAsynchronous(true); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000")); - - // First test that we fail to open the event. - std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", - target.process_id()); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); - - // Now successfully open the event after adding a duplicate handle rule. - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES, - TargetPolicy::HANDLES_DUP_ANY, - L"Event")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str())); -} - -// Tests that duplicating an object works only when the policy allows it. -TEST(HandlePolicyTest, DuplicatePeerHandle) { - TestRunner target; - TestRunner runner; - - // Kick off an asynchronous target process for testing. - target.SetAsynchronous(true); - target.SetUnsandboxed(true); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000")); - - // First test that we fail to open the event. - std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", - target.process_id()); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); - - // Now successfully open the event after adding a duplicate handle rule. - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES, - TargetPolicy::HANDLES_DUP_ANY, - L"Event")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str())); -} - -// Tests that duplicating an object works only when the policy allows it. -TEST(HandlePolicyTest, DuplicateBrokerHandle) { - TestRunner runner; - - // First test that we fail to open the event. - std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", - ::GetCurrentProcessId()); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); - - // Add the peer rule and make sure we fail again. - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES, - TargetPolicy::HANDLES_DUP_ANY, - L"Event")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); - - - // Now successfully open the event after adding a broker handle rule. - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES, - TargetPolicy::HANDLES_DUP_BROKER, - L"Event")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str())); -} - -} // namespace sandbox - diff --git a/sandbox/src/handle_table.cc b/sandbox/src/handle_table.cc deleted file mode 100644 index c7fcf0a..0000000 --- a/sandbox/src/handle_table.cc +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/handle_table.h" - -#include -#include - -#include "base/memory/scoped_ptr.h" -#include "sandbox/src/win_utils.h" - -namespace { - -bool CompareHandleEntries(const SYSTEM_HANDLE_INFORMATION& a, - const SYSTEM_HANDLE_INFORMATION& b) { - return a.ProcessId < b.ProcessId; -} - -} // namespace - -namespace sandbox { - -const char16* HandleTable::kTypeProcess = L"Process"; -const char16* HandleTable::kTypeThread = L"Thread"; -const char16* HandleTable::kTypeFile = L"File"; -const char16* HandleTable::kTypeDirectory = L"Directory"; -const char16* HandleTable::kTypeKey = L"Key"; -const char16* HandleTable::kTypeWindowStation = L"WindowStation"; -const char16* HandleTable::kTypeDesktop = L"Desktop"; -const char16* HandleTable::kTypeService = L"Service"; -const char16* HandleTable::kTypeMutex = L"Mutex"; -const char16* HandleTable::kTypeSemaphore = L"Semaphore"; -const char16* HandleTable::kTypeEvent = L"Event"; -const char16* HandleTable::kTypeTimer = L"Timer"; -const char16* HandleTable::kTypeNamedPipe = L"NamedPipe"; -const char16* HandleTable::kTypeJobObject = L"JobObject"; -const char16* HandleTable::kTypeFileMap = L"FileMap"; -const char16* HandleTable::kTypeAlpcPort = L"ALPC Port"; - -HandleTable::HandleTable() { - static NtQuerySystemInformation QuerySystemInformation = NULL; - if (!QuerySystemInformation) - ResolveNTFunctionPtr("NtQuerySystemInformation", &QuerySystemInformation); - - ULONG size = 0x15000; - NTSTATUS result; - do { - handle_info_buffer_.resize(size); - result = QuerySystemInformation(SystemHandleInformation, - handle_info_internal(), size, &size); - } while (result == STATUS_INFO_LENGTH_MISMATCH); - - // We failed, so make an empty table. - if (!NT_SUCCESS(result)) { - handle_info_buffer_.resize(0); - return; - } - - // Sort it to make process lookups faster. - std::sort(handle_info_internal()->Information, - handle_info_internal()->Information + - handle_info_internal()->NumberOfHandles, CompareHandleEntries); -} - -HandleTable::Iterator HandleTable::HandlesForProcess(ULONG process_id) const { - SYSTEM_HANDLE_INFORMATION key; - key.ProcessId = process_id; - - const SYSTEM_HANDLE_INFORMATION* start = handle_info()->Information; - const SYSTEM_HANDLE_INFORMATION* finish = - &handle_info()->Information[handle_info()->NumberOfHandles]; - - start = std::lower_bound(start, finish, key, CompareHandleEntries); - if (start->ProcessId != process_id) - return Iterator(*this, finish, finish); - finish = std::upper_bound(start, finish, key, CompareHandleEntries); - return Iterator(*this, start, finish); -} - -HandleTable::HandleEntry::HandleEntry( - const SYSTEM_HANDLE_INFORMATION* handle_info_entry) - : handle_entry_(handle_info_entry), last_entry_(0) { -} - -void HandleTable::HandleEntry::UpdateInfo(UpdateType flag) { - static NtQueryObject QueryObject = NULL; - if (!QueryObject) - ResolveNTFunctionPtr("NtQueryObject", &QueryObject); - - NTSTATUS result; - - // Always update the basic type info, but grab the names as needed. - if (needs_info_update()) { - handle_name_.clear(); - type_name_.clear(); - last_entry_ = handle_entry_; - - // Most handle names are very short, so start small and reuse this buffer. - if (type_info_buffer_.empty()) - type_info_buffer_.resize(sizeof(OBJECT_TYPE_INFORMATION) - + (32 * sizeof(wchar_t))); - ULONG size = static_cast(type_info_buffer_.size()); - result = QueryObject(reinterpret_cast(handle_entry_->Handle), - ObjectTypeInformation, type_info_internal(), size, &size); - while (result == STATUS_INFO_LENGTH_MISMATCH) { - type_info_buffer_.resize(size); - result = QueryObject(reinterpret_cast(handle_entry_->Handle), - ObjectTypeInformation, type_info_internal(), size, &size); - } - - if (!NT_SUCCESS(result)) { - type_info_buffer_.clear(); - return; - } - } - - // Don't bother copying out names until we ask for them, and then cache them. - switch (flag) { - case UPDATE_INFO_AND_NAME: - if (type_info_buffer_.size() && handle_name_.empty()) { - ULONG size = MAX_PATH; - scoped_ptr name; - do { - name.reset(reinterpret_cast(new BYTE[size])); - result = QueryObject(reinterpret_cast( - handle_entry_->Handle), ObjectNameInformation, name.get(), - size, &size); - } while (result == STATUS_INFO_LENGTH_MISMATCH); - - if (NT_SUCCESS(result)) { - handle_name_.assign(name->Buffer, name->Length / sizeof(wchar_t)); - } - } - break; - - case UPDATE_INFO_AND_TYPE_NAME: - if (!type_info_buffer_.empty() && type_info_internal()->Name.Buffer && - type_name_.empty()) { - type_name_.assign(type_info_internal()->Name.Buffer, - type_info_internal()->Name.Length / sizeof(wchar_t)); - } - break; - } -} - -const OBJECT_TYPE_INFORMATION* HandleTable::HandleEntry::TypeInfo() { - UpdateInfo(UPDATE_INFO_ONLY); - return type_info_buffer_.empty() ? NULL : type_info_internal(); -} - -const string16& HandleTable::HandleEntry::Name() { - UpdateInfo(UPDATE_INFO_AND_NAME); - return handle_name_; -} - -const string16& HandleTable::HandleEntry::Type() { - UpdateInfo(UPDATE_INFO_AND_TYPE_NAME); - return type_name_; -} - -bool HandleTable::HandleEntry::IsType(const string16& type_string) { - UpdateInfo(UPDATE_INFO_ONLY); - if (type_info_buffer_.empty()) - return false; - return type_string.compare(0, - type_info_internal()->Name.Length / sizeof(wchar_t), - type_info_internal()->Name.Buffer) == 0; -} - -HandleTable::Iterator::Iterator(const HandleTable& table, - const SYSTEM_HANDLE_INFORMATION* start, - const SYSTEM_HANDLE_INFORMATION* end) - : table_(table), current_(start), end_(end) { -} - -HandleTable::Iterator::Iterator(const Iterator& it) - : table_(it.table_), current_(it.current_.handle_entry_), end_(it.end_) { -} - -} // namespace sandbox diff --git a/sandbox/src/handle_table.h b/sandbox/src/handle_table.h deleted file mode 100644 index e2b2615..0000000 --- a/sandbox/src/handle_table.h +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_HANDLE_TABLE_H_ -#define SANDBOX_SRC_HANDLE_TABLE_H_ - -#include -#include - -#include "base/basictypes.h" -#include "base/string16.h" -#include "sandbox/src/nt_internals.h" - -namespace sandbox { - -// HandleTable retrieves the global handle table and provides helper classes -// for iterating through the table and retrieving handle info. -class HandleTable { - public: - static const char16* HandleTable::kTypeProcess; - static const char16* HandleTable::kTypeThread; - static const char16* HandleTable::kTypeFile; - static const char16* HandleTable::kTypeDirectory; - static const char16* HandleTable::kTypeKey; - static const char16* HandleTable::kTypeWindowStation; - static const char16* HandleTable::kTypeDesktop; - static const char16* HandleTable::kTypeService; - static const char16* HandleTable::kTypeMutex; - static const char16* HandleTable::kTypeSemaphore; - static const char16* HandleTable::kTypeEvent; - static const char16* HandleTable::kTypeTimer; - static const char16* HandleTable::kTypeNamedPipe; - static const char16* HandleTable::kTypeJobObject; - static const char16* HandleTable::kTypeFileMap; - static const char16* HandleTable::kTypeAlpcPort; - - class Iterator; - - // Used by the iterator to provide simple caching accessors to handle data. - class HandleEntry { - public: - bool operator==(const HandleEntry& rhs) const { - return handle_entry_ == rhs.handle_entry_; - } - - bool operator!=(const HandleEntry& rhs) const { - return handle_entry_ != rhs.handle_entry_; - } - - const SYSTEM_HANDLE_INFORMATION* handle_entry() const { - return handle_entry_; - } - - const OBJECT_TYPE_INFORMATION* TypeInfo(); - - const string16& Name(); - - const string16& Type(); - - bool IsType(const string16& type_string); - - private: - friend class Iterator; - friend class HandleTable; - - enum UpdateType { - UPDATE_INFO_ONLY, - UPDATE_INFO_AND_NAME, - UPDATE_INFO_AND_TYPE_NAME, - }; - - explicit HandleEntry(const SYSTEM_HANDLE_INFORMATION* handle_info_entry); - - bool needs_info_update() { return handle_entry_ != last_entry_; } - - void UpdateInfo(UpdateType flag); - - OBJECT_TYPE_INFORMATION* type_info_internal() { - return reinterpret_cast( - &(type_info_buffer_[0])); - } - - const SYSTEM_HANDLE_INFORMATION* handle_entry_; - const SYSTEM_HANDLE_INFORMATION* last_entry_; - std::vector type_info_buffer_; - string16 handle_name_; - string16 type_name_; - - DISALLOW_COPY_AND_ASSIGN(HandleEntry); - }; - - class Iterator { - public: - Iterator(const HandleTable& table, const SYSTEM_HANDLE_INFORMATION* start, - const SYSTEM_HANDLE_INFORMATION* stop); - - Iterator(const Iterator& it); - - Iterator& operator++() { - if (++(current_.handle_entry_) == end_) - current_.handle_entry_ = table_.end(); - return *this; - } - - bool operator==(const Iterator& rhs) const { - return current_ == rhs.current_; - } - - bool operator!=(const Iterator& rhs) const { - return current_ != rhs.current_; - } - - HandleEntry& operator*() { return current_; } - - operator const SYSTEM_HANDLE_INFORMATION*() { - return current_.handle_entry_; - } - - HandleEntry* operator->() { return ¤t_; } - - private: - const HandleTable& table_; - HandleEntry current_; - const SYSTEM_HANDLE_INFORMATION* end_; - }; - - HandleTable(); - - Iterator begin() const { - return Iterator(*this, handle_info()->Information, - &handle_info()->Information[handle_info()->NumberOfHandles]); - } - - const SYSTEM_HANDLE_INFORMATION_EX* handle_info() const { - return reinterpret_cast( - &(handle_info_buffer_[0])); - } - - // Returns an iterator to the handles for only the supplied process ID. - Iterator HandlesForProcess(ULONG process_id) const; - const SYSTEM_HANDLE_INFORMATION* end() const { - return &handle_info()->Information[handle_info()->NumberOfHandles]; - } - - private: - SYSTEM_HANDLE_INFORMATION_EX* handle_info_internal() { - return reinterpret_cast( - &(handle_info_buffer_[0])); - } - - std::vector handle_info_buffer_; - - DISALLOW_COPY_AND_ASSIGN(HandleTable); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_HANDLE_TABLE_H_ diff --git a/sandbox/src/integrity_level_test.cc b/sandbox/src/integrity_level_test.cc deleted file mode 100644 index c6e0b64..0000000 --- a/sandbox/src/integrity_level_test.cc +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "base/win/windows_version.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/tests/common/controller.h" - -namespace sandbox { - - -SBOX_TESTS_COMMAND int CheckIntegrityLevel(int argc, wchar_t **argv) { - ATL::CAccessToken token; - if (!token.GetEffectiveToken(TOKEN_READ)) - return SBOX_TEST_FAILED; - - char* buffer[100]; - DWORD buf_size = 100; - if (!::GetTokenInformation(token.GetHandle(), TokenIntegrityLevel, - reinterpret_cast(buffer), buf_size, - &buf_size)) - return SBOX_TEST_FAILED; - - TOKEN_MANDATORY_LABEL* label = - reinterpret_cast(buffer); - - PSID sid_low = NULL; - if (!::ConvertStringSidToSid(L"S-1-16-4096", &sid_low)) - return SBOX_TEST_FAILED; - - BOOL is_low_sid = ::EqualSid(label->Label.Sid, sid_low); - - ::LocalFree(sid_low); - - if (is_low_sid) - return SBOX_TEST_SUCCEEDED; - - return SBOX_TEST_DENIED; -} - -TEST(IntegrityLevelTest, TestLowILReal) { - if (base::win::GetVersion() != base::win::VERSION_VISTA) - return; - - TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE); - - runner.SetTimeout(INFINITE); - - runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel")); - - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel")); -} - -TEST(DelayedIntegrityLevelTest, TestLowILDelayed) { - if (base::win::GetVersion() != base::win::VERSION_VISTA) - return; - - TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE); - - runner.SetTimeout(INFINITE); - - runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel")); - - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel")); -} - -TEST(IntegrityLevelTest, TestNoILChange) { - if (base::win::GetVersion() != base::win::VERSION_VISTA) - return; - - TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE); - - runner.SetTimeout(INFINITE); - - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel")); -} - -} // namespace sandbox diff --git a/sandbox/src/interception.cc b/sandbox/src/interception.cc deleted file mode 100644 index a850c6e..0000000 --- a/sandbox/src/interception.cc +++ /dev/null @@ -1,551 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// For information about interceptions as a whole see -// http://dev.chromium.org/developers/design-documents/sandbox . - -#include - -#include "sandbox/src/interception.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/win/pe_image.h" -#include "base/win/windows_version.h" -#include "sandbox/src/interception_internal.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_utils.h" -#include "sandbox/src/service_resolver.h" -#include "sandbox/src/target_interceptions.h" -#include "sandbox/src/target_process.h" -#include "sandbox/src/wow64.h" - -namespace { - -const char kMapViewOfSectionName[] = "NtMapViewOfSection"; -const char kUnmapViewOfSectionName[] = "NtUnmapViewOfSection"; - -// Standard allocation granularity and page size for Windows. -const size_t kAllocGranularity = 65536; -const size_t kPageSize = 4096; - -// Find a random offset within 64k and aligned to ceil(log2(size)). -size_t GetGranularAlignedRandomOffset(size_t size) { - CHECK_LE(size, kAllocGranularity); - unsigned int offset; - - do { - rand_s(&offset); - offset &= (kAllocGranularity - 1); - } while (offset > (kAllocGranularity - size)); - - // Find an alignment between 64 and the page size (4096). - size_t align_size = kPageSize; - for (size_t new_size = align_size / 2; new_size >= size; new_size /= 2) { - align_size = new_size; - } - return offset & ~(align_size - 1); -} - -} // namespace - -namespace sandbox { - -SANDBOX_INTERCEPT SharedMemory* g_interceptions; - -// Table of the unpatched functions that we intercept. Mapped from the parent. -SANDBOX_INTERCEPT OriginalFunctions g_originals = { NULL }; - -// Magic constant that identifies that this function is not to be patched. -const char kUnloadDLLDummyFunction[] = "@"; - -InterceptionManager::InterceptionManager(TargetProcess* child_process, - bool relaxed) - : child_(child_process), names_used_(false), relaxed_(relaxed) { - child_->AddRef(); -} -InterceptionManager::~InterceptionManager() { - child_->Release(); -} - -bool InterceptionManager::AddToPatchedFunctions( - const wchar_t* dll_name, const char* function_name, - InterceptionType interception_type, const void* replacement_code_address, - InterceptorId id) { - InterceptionData function; - function.type = interception_type; - function.id = id; - function.dll = dll_name; - function.function = function_name; - function.interceptor_address = replacement_code_address; - - interceptions_.push_back(function); - return true; -} - -bool InterceptionManager::AddToPatchedFunctions( - const wchar_t* dll_name, const char* function_name, - InterceptionType interception_type, const char* replacement_function_name, - InterceptorId id) { - InterceptionData function; - function.type = interception_type; - function.id = id; - function.dll = dll_name; - function.function = function_name; - function.interceptor = replacement_function_name; - function.interceptor_address = NULL; - - interceptions_.push_back(function); - names_used_ = true; - return true; -} - -bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) { - InterceptionData module_to_unload; - module_to_unload.type = INTERCEPTION_UNLOAD_MODULE; - module_to_unload.dll = dll_name; - // The next two are dummy values that make the structures regular, instead - // of having special cases. They should not be used. - module_to_unload.function = kUnloadDLLDummyFunction; - module_to_unload.interceptor_address = reinterpret_cast(1); - - interceptions_.push_back(module_to_unload); - return true; -} - -bool InterceptionManager::InitializeInterceptions() { - if (interceptions_.empty()) - return true; // Nothing to do here - - size_t buffer_bytes = GetBufferSize(); - scoped_array local_buffer(new char[buffer_bytes]); - - if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes)) - return false; - - void* remote_buffer; - if (!CopyDataToChild(local_buffer.get(), buffer_bytes, &remote_buffer)) - return false; - - bool hot_patch_needed = (0 != buffer_bytes); - if (!PatchNtdll(hot_patch_needed)) - return false; - - g_interceptions = reinterpret_cast(remote_buffer); - ResultCode rc = child_->TransferVariable("g_interceptions", - &g_interceptions, - sizeof(g_interceptions)); - return (SBOX_ALL_OK == rc); -} - -size_t InterceptionManager::GetBufferSize() const { - std::set dlls; - size_t buffer_bytes = 0; - - std::list::const_iterator it = interceptions_.begin(); - for (; it != interceptions_.end(); ++it) { - // skip interceptions that are performed from the parent - if (!IsInterceptionPerformedByChild(*it)) - continue; - - if (!dlls.count(it->dll)) { - // NULL terminate the dll name on the structure - size_t dll_name_bytes = (it->dll.size() + 1) * sizeof(wchar_t); - - // include the dll related size - buffer_bytes += RoundUpToMultiple(offsetof(DllPatchInfo, dll_name) + - dll_name_bytes, sizeof(size_t)); - dlls.insert(it->dll); - } - - // we have to NULL terminate the strings on the structure - size_t strings_chars = it->function.size() + it->interceptor.size() + 2; - - // a new FunctionInfo is required per function - size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars; - record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t)); - buffer_bytes += record_bytes; - } - - if (0 != buffer_bytes) - // add the part of SharedMemory that we have not counted yet - buffer_bytes += offsetof(SharedMemory, dll_list); - - return buffer_bytes; -} - -// Basically, walk the list of interceptions moving them to the config buffer, -// but keeping together all interceptions that belong to the same dll. -// The config buffer is a local buffer, not the one allocated on the child. -bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) { - if (0 == buffer_bytes) - return true; - - DCHECK(buffer_bytes > sizeof(SharedMemory)); - - SharedMemory* shared_memory = reinterpret_cast(buffer); - DllPatchInfo* dll_info = shared_memory->dll_list; - int num_dlls = 0; - - shared_memory->interceptor_base = names_used_ ? child_->MainModule() : NULL; - - buffer_bytes -= offsetof(SharedMemory, dll_list); - buffer = dll_info; - - std::list::iterator it = interceptions_.begin(); - for (; it != interceptions_.end();) { - // skip interceptions that are performed from the parent - if (!IsInterceptionPerformedByChild(*it)) { - ++it; - continue; - } - - const std::wstring dll = it->dll; - if (!SetupDllInfo(*it, &buffer, &buffer_bytes)) - return false; - - // walk the interceptions from this point, saving the ones that are - // performed on this dll, and removing the entry from the list. - // advance the iterator before removing the element from the list - std::list::iterator rest = it; - for (; rest != interceptions_.end();) { - if (rest->dll == dll) { - if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info)) - return false; - if (it == rest) - ++it; - rest = interceptions_.erase(rest); - } else { - ++rest; - } - } - dll_info = reinterpret_cast(buffer); - ++num_dlls; - } - - shared_memory->num_intercepted_dlls = num_dlls; - return true; -} - -// Fills up just the part that depends on the dll, not the info that depends on -// the actual interception. -bool InterceptionManager::SetupDllInfo(const InterceptionData& data, - void** buffer, - size_t* buffer_bytes) const { - DCHECK(buffer_bytes); - DCHECK(buffer); - DCHECK(*buffer); - - DllPatchInfo* dll_info = reinterpret_cast(*buffer); - - // the strings have to be zero terminated - size_t required = offsetof(DllPatchInfo, dll_name) + - (data.dll.size() + 1) * sizeof(wchar_t); - required = RoundUpToMultiple(required, sizeof(size_t)); - if (*buffer_bytes < required) - return false; - - *buffer_bytes -= required; - *buffer = reinterpret_cast(*buffer) + required; - - // set up the dll info to be what we know about it at this time - dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE); - dll_info->record_bytes = required; - dll_info->offset_to_functions = required; - dll_info->num_functions = 0; - data.dll._Copy_s(dll_info->dll_name, data.dll.size(), data.dll.size()); - dll_info->dll_name[data.dll.size()] = L'\0'; - - return true; -} - -bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data, - void** buffer, - size_t* buffer_bytes, - DllPatchInfo* dll_info) const { - DCHECK(buffer_bytes); - DCHECK(buffer); - DCHECK(*buffer); - - if ((dll_info->unload_module) && - (data.function != kUnloadDLLDummyFunction)) { - // Can't specify a dll for both patch and unload. - NOTREACHED(); - } - - FunctionInfo* function = reinterpret_cast(*buffer); - - size_t name_bytes = data.function.size(); - size_t interceptor_bytes = data.interceptor.size(); - - // the strings at the end of the structure are zero terminated - size_t required = offsetof(FunctionInfo, function) + - name_bytes + interceptor_bytes + 2; - required = RoundUpToMultiple(required, sizeof(size_t)); - if (*buffer_bytes < required) - return false; - - // update the caller's values - *buffer_bytes -= required; - *buffer = reinterpret_cast(*buffer) + required; - - function->record_bytes = required; - function->type = data.type; - function->id = data.id; - function->interceptor_address = data.interceptor_address; - char* names = function->function; - - data.function._Copy_s(names, name_bytes, name_bytes); - names += name_bytes; - *names++ = '\0'; - - // interceptor follows the function_name - data.interceptor._Copy_s(names, interceptor_bytes, interceptor_bytes); - names += interceptor_bytes; - *names++ = '\0'; - - // update the dll table - dll_info->num_functions++; - dll_info->record_bytes += required; - - return true; -} - -bool InterceptionManager::CopyDataToChild(const void* local_buffer, - size_t buffer_bytes, - void** remote_buffer) const { - DCHECK(NULL != remote_buffer); - if (0 == buffer_bytes) { - *remote_buffer = NULL; - return true; - } - - HANDLE child = child_->Process(); - - // Allocate memory on the target process without specifying the address - void* remote_data = ::VirtualAllocEx(child, NULL, buffer_bytes, - MEM_COMMIT, PAGE_READWRITE); - if (NULL == remote_data) - return false; - - SIZE_T bytes_written; - BOOL success = ::WriteProcessMemory(child, remote_data, local_buffer, - buffer_bytes, &bytes_written); - if (FALSE == success || bytes_written != buffer_bytes) { - ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE); - return false; - } - - *remote_buffer = remote_data; - - return true; -} - -// Only return true if the child should be able to perform this interception. -bool InterceptionManager::IsInterceptionPerformedByChild( - const InterceptionData& data) const { - if (INTERCEPTION_INVALID == data.type) - return false; - - if (INTERCEPTION_SERVICE_CALL == data.type) - return false; - - if (data.type >= INTERCEPTION_LAST) - return false; - - std::wstring ntdll(kNtdllName); - if (ntdll == data.dll) - return false; // ntdll has to be intercepted from the parent - - return true; -} - -bool InterceptionManager::PatchNtdll(bool hot_patch_needed) { - // Maybe there is nothing to do - if (!hot_patch_needed && interceptions_.empty()) - return true; - - if (hot_patch_needed) { -#if SANDBOX_EXPORTS - // Make sure the functions are not excluded by the linker. -#if defined(_WIN64) - #pragma comment(linker, "/include:TargetNtMapViewOfSection64") - #pragma comment(linker, "/include:TargetNtUnmapViewOfSection64") -#else - #pragma comment(linker, "/include:_TargetNtMapViewOfSection@44") - #pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12") -#endif -#endif - ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44); - ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12); - } - - // Reserve a full 64k memory range in the child process. - HANDLE child = child_->Process(); - BYTE* thunk_base = reinterpret_cast( - ::VirtualAllocEx(child, NULL, kAllocGranularity, - MEM_RESERVE, PAGE_NOACCESS)); - - // Find an aligned, random location within the reserved range. - size_t thunk_bytes = interceptions_.size() * sizeof(ThunkData) + - sizeof(DllInterceptionData); - size_t thunk_offset = GetGranularAlignedRandomOffset(thunk_bytes); - - // Split the base and offset along page boundaries. - thunk_base += thunk_offset & ~(kPageSize - 1); - thunk_offset &= kPageSize - 1; - - // Make an aligned, padded allocation, and move the pointer to our chunk. - size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & kPageSize; - thunk_base = reinterpret_cast( - ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded, - MEM_COMMIT, PAGE_EXECUTE_READWRITE)); - CHECK(thunk_base); // If this fails we'd crash anyway on an invalid access. - DllInterceptionData* thunks = reinterpret_cast( - thunk_base + thunk_offset); - - DllInterceptionData dll_data; - dll_data.data_bytes = thunk_bytes; - dll_data.num_thunks = 0; - dll_data.used_bytes = offsetof(DllInterceptionData, thunks); - - // Reset all helpers for a new child. - memset(g_originals, 0, sizeof(g_originals)); - - // this should write all the individual thunks to the child's memory - if (!PatchClientFunctions(thunks, thunk_bytes, &dll_data)) - return false; - - // and now write the first part of the table to the child's memory - SIZE_T written; - bool ok = FALSE != ::WriteProcessMemory(child, thunks, &dll_data, - offsetof(DllInterceptionData, thunks), - &written); - - if (!ok || (offsetof(DllInterceptionData, thunks) != written)) - return false; - - // Attempt to protect all the thunks, but ignore failure - DWORD old_protection; - ::VirtualProtectEx(child, thunks, thunk_bytes, - PAGE_EXECUTE_READ, &old_protection); - - ResultCode ret = child_->TransferVariable("g_originals", g_originals, - sizeof(g_originals)); - - return SBOX_ALL_OK == ret ? true : false; -} - -bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks, - size_t thunk_bytes, - DllInterceptionData* dll_data) { - DCHECK(NULL != thunks); - DCHECK(NULL != dll_data); - - HMODULE ntdll_base = ::GetModuleHandle(kNtdllName); - if (!ntdll_base) - return false; - - base::win::PEImage ntdll_image(ntdll_base); - - // Bypass purify's interception. - wchar_t* loader_get = reinterpret_cast( - ntdll_image.GetProcAddress("LdrGetDllHandle")); - if (loader_get) { - if (!GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - loader_get, &ntdll_base)) - return false; - } - - if (base::win::GetVersion() <= base::win::VERSION_VISTA) { - Wow64 WowHelper(child_, ntdll_base); - if (!WowHelper.WaitForNtdll()) - return false; - } - - char* interceptor_base = NULL; - -#if SANDBOX_EXPORTS - interceptor_base = reinterpret_cast(child_->MainModule()); - HMODULE local_interceptor = ::LoadLibrary(child_->Name()); -#endif - - ServiceResolverThunk* thunk; -#if defined(_WIN64) - thunk = new ServiceResolverThunk(child_->Process(), relaxed_); -#else - base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); - if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { - if (os_info->version() >= base::win::VERSION_WIN8) - thunk = new Wow64W8ResolverThunk(child_->Process(), relaxed_); - else - thunk = new Wow64ResolverThunk(child_->Process(), relaxed_); - } else if (!IsXPSP2OrLater()) { - thunk = new Win2kResolverThunk(child_->Process(), relaxed_); - } else if (os_info->version() >= base::win::VERSION_WIN8) { - thunk = new Win8ResolverThunk(child_->Process(), relaxed_); - } else { - thunk = new ServiceResolverThunk(child_->Process(), relaxed_); - } -#endif - - std::list::iterator it = interceptions_.begin(); - for (; it != interceptions_.end(); ++it) { - const std::wstring ntdll(kNtdllName); - if (it->dll != ntdll) - break; - - if (INTERCEPTION_SERVICE_CALL != it->type) - break; - -#if SANDBOX_EXPORTS - // We may be trying to patch by function name. - if (NULL == it->interceptor_address) { - const char* address; - NTSTATUS ret = thunk->ResolveInterceptor(local_interceptor, - it->interceptor.c_str(), - reinterpret_cast( - &address)); - if (!NT_SUCCESS(ret)) - break; - - // Translate the local address to an address on the child. - it->interceptor_address = interceptor_base + (address - - reinterpret_cast(local_interceptor)); - } -#endif - NTSTATUS ret = thunk->Setup(ntdll_base, - interceptor_base, - it->function.c_str(), - it->interceptor.c_str(), - it->interceptor_address, - &thunks->thunks[dll_data->num_thunks], - thunk_bytes - dll_data->used_bytes, - NULL); - if (!NT_SUCCESS(ret)) - break; - - DCHECK(!g_originals[it->id]); - g_originals[it->id] = &thunks->thunks[dll_data->num_thunks]; - - dll_data->num_thunks++; - dll_data->used_bytes += sizeof(ThunkData); - } - - delete(thunk); - -#if SANDBOX_EXPORTS - if (NULL != local_interceptor) - ::FreeLibrary(local_interceptor); -#endif - - if (it != interceptions_.end()) - return false; - - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/interception.h b/sandbox/src/interception.h deleted file mode 100644 index c02b0ff..0000000 --- a/sandbox/src/interception.h +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Defines InterceptionManager, the class in charge of setting up interceptions -// for the sandboxed process. For more details see -// http://dev.chromium.org/developers/design-documents/sandbox . - -#ifndef SANDBOX_SRC_INTERCEPTION_H_ -#define SANDBOX_SRC_INTERCEPTION_H_ - -#include -#include - -#include "base/basictypes.h" -#include "base/gtest_prod_util.h" -#include "sandbox/src/sandbox_types.h" - -namespace sandbox { - -class TargetProcess; -enum InterceptorId; - -// Internal structures used for communication between the broker and the target. -struct DllPatchInfo; -struct DllInterceptionData; - -// The InterceptionManager executes on the parent application, and it is in -// charge of setting up the desired interceptions, and placing the Interception -// Agent into the child application. -// -// The exposed API consists of two methods: AddToPatchedFunctions to set up a -// particular interception, and InitializeInterceptions to actually go ahead and -// perform all interceptions and transfer data to the child application. -// -// The typical usage is something like this: -// -// InterceptionManager interception_manager(child); -// if (!interception_manager.AddToPatchedFunctions( -// L"ntdll.dll", "NtCreateFile", -// sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1)) -// return false; -// -// if (!interception_manager.AddToPatchedFunctions( -// L"kernel32.dll", "CreateDirectoryW", -// sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2)) -// return false; -// -// if (!interception_manager.InitializeInterceptions()) { -// DWORD error = ::GetLastError(); -// return false; -// } -// -// Any required syncronization must be performed outside this class. Also, it is -// not possible to perform further interceptions after InitializeInterceptions -// is called. -// -class InterceptionManager { - // The unit test will access private members. - // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes - // do not work with sandbox tests. - FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1); - FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2); - - public: - // An interception manager performs interceptions on a given child process. - // If we are allowed to intercept functions that have been patched by somebody - // else, relaxed should be set to true. - // Note: We increase the child's reference count internally. - InterceptionManager(TargetProcess* child_process, bool relaxed); - ~InterceptionManager(); - - // Patches function_name inside dll_name to point to replacement_code_address. - // function_name has to be an exported symbol of dll_name. - // Returns true on success. - // - // The new function should match the prototype and calling convention of the - // function to intercept except for one extra argument (the first one) that - // contains a pointer to the original function, to simplify the development - // of interceptors (for IA32). In x64, there is no extra argument to the - // interceptor, so the provided InterceptorId is used to keep a table of - // intercepted functions so that the interceptor can index that table to get - // the pointer that would have been the first argument (g_originals[id]). - // - // For example, to intercept NtClose, the following code could be used: - // - // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); - // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose, - // IN HANDLE Handle) { - // // do something - // // call the original function - // return OriginalClose(Handle); - // } - // - // And in x64: - // - // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); - // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) { - // // do something - // // call the original function - // NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID]; - // return OriginalClose(Handle); - // } - bool AddToPatchedFunctions(const wchar_t* dll_name, - const char* function_name, - InterceptionType interception_type, - const void* replacement_code_address, - InterceptorId id); - - // Patches function_name inside dll_name to point to - // replacement_function_name. - bool AddToPatchedFunctions(const wchar_t* dll_name, - const char* function_name, - InterceptionType interception_type, - const char* replacement_function_name, - InterceptorId id); - - // The interception agent will unload the dll with dll_name. - bool AddToUnloadModules(const wchar_t* dll_name); - - // Initializes all interceptions on the client. - // Returns true on success. - // - // The child process must be created suspended, and cannot be resumed until - // after this method returns. In addition, no action should be performed on - // the child that may cause it to resume momentarily, such as injecting - // threads or APCs. - // - // This function must be called only once, after all interceptions have been - // set up using AddToPatchedFunctions. - bool InitializeInterceptions(); - - private: - // Used to store the interception information until the actual set-up. - struct InterceptionData { - InterceptionType type; // Interception type. - InterceptorId id; // Interceptor id. - std::wstring dll; // Name of dll to intercept. - std::string function; // Name of function to intercept. - std::string interceptor; // Name of interceptor function. - const void* interceptor_address; // Interceptor's entry point. - }; - - // Calculates the size of the required configuration buffer. - size_t GetBufferSize() const; - - // Rounds up the size of a given buffer, considering alignment (padding). - // value is the current size of the buffer, and alignment is specified in - // bytes. - static inline size_t RoundUpToMultiple(size_t value, size_t alignment) { - return ((value + alignment -1) / alignment) * alignment; - } - - // Sets up a given buffer with all the information that has to be transfered - // to the child. - // Returns true on success. - // - // The buffer size should be at least the value returned by GetBufferSize - bool SetupConfigBuffer(void* buffer, size_t buffer_bytes); - - // Fills up the part of the transfer buffer that corresponds to information - // about one dll to patch. - // data is the first recorded interception for this dll. - // Returns true on success. - // - // On successful return, buffer will be advanced from it's current position - // to the point where the next block of configuration data should be written - // (the actual interception info), and the current size of the buffer will - // decrease to account the space used by this method. - bool SetupDllInfo(const InterceptionData& data, - void** buffer, size_t* buffer_bytes) const; - - // Fills up the part of the transfer buffer that corresponds to a single - // function to patch. - // dll_info points to the dll being updated with the interception stored on - // data. The buffer pointer and remaining size are updated by this call. - // Returns true on success. - bool SetupInterceptionInfo(const InterceptionData& data, void** buffer, - size_t* buffer_bytes, - DllPatchInfo* dll_info) const; - - // Returns true if this interception is to be performed by the child - // as opposed to from the parent. - bool IsInterceptionPerformedByChild(const InterceptionData& data) const; - - // Allocates a buffer on the child's address space (returned on - // remote_buffer), and fills it with the contents of a local buffer. - // Returns true on success. - bool CopyDataToChild(const void* local_buffer, size_t buffer_bytes, - void** remote_buffer) const; - - // Performs the cold patch (from the parent) of ntdll. - // Returns true on success. - // - // This method will insert additional interceptions to launch the interceptor - // agent on the child process, if there are additional interceptions to do. - bool PatchNtdll(bool hot_patch_needed); - - // Peforms the actual interceptions on ntdll. - // thunks is the memory to store all the thunks for this dll (on the child), - // and dll_data is a local buffer to hold global dll interception info. - // Returns true on success. - bool PatchClientFunctions(DllInterceptionData* thunks, - size_t thunk_bytes, - DllInterceptionData* dll_data); - - // The process to intercept. - TargetProcess* child_; - // Holds all interception info until the call to initialize (perform the - // actual patch). - std::list interceptions_; - - // Keep track of patches added by name. - bool names_used_; - - // true if we are allowed to patch already-patched functions. - bool relaxed_; - - DISALLOW_COPY_AND_ASSIGN(InterceptionManager); -}; - -// This macro simply calls interception_manager.AddToPatchedFunctions with -// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that -// the interceptor is called "TargetXXX", where XXX is the name of the service. -// Note that num_params is the number of bytes to pop out of the stack for -// the exported interceptor, following the calling convention of a service call -// (WINAPI = with the "C" underscore). -#if SANDBOX_EXPORTS -#if defined(_WIN64) -#define MAKE_SERVICE_NAME(service, params) "Target" # service "64" -#else -#define MAKE_SERVICE_NAME(service, params) "_Target" # service "@" # params -#endif - -#define ADD_NT_INTERCEPTION(service, id, num_params) \ - AddToPatchedFunctions(kNtdllName, #service, \ - sandbox::INTERCEPTION_SERVICE_CALL, \ - MAKE_SERVICE_NAME(service, num_params), id) - -#define INTERCEPT_NT(manager, service, id, num_params) \ - ((&Target##service) ? \ - manager->ADD_NT_INTERCEPTION(service, id, num_params) : false) - -#define INTERCEPT_EAT(manager, dll, function, id, num_params) \ - ((&Target##function) ? \ - manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ - MAKE_SERVICE_NAME(function, num_params), \ - id) : \ - false) -#else // SANDBOX_EXPORTS -#if defined(_WIN64) -#define MAKE_SERVICE_NAME(service) &Target##service##64 -#else -#define MAKE_SERVICE_NAME(service) &Target##service -#endif - -#define ADD_NT_INTERCEPTION(service, id, num_params) \ - AddToPatchedFunctions(kNtdllName, #service, \ - sandbox::INTERCEPTION_SERVICE_CALL, \ - MAKE_SERVICE_NAME(service), id) - -#define INTERCEPT_NT(manager, service, id, num_params) \ - manager->ADD_NT_INTERCEPTION(service, id, num_params) - -#define INTERCEPT_EAT(manager, dll, function, id, num_params) \ - manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ - MAKE_SERVICE_NAME(function), id) -#endif // SANDBOX_EXPORTS - -} // namespace sandbox - -#endif // SANDBOX_SRC_INTERCEPTION_H_ diff --git a/sandbox/src/interception_agent.cc b/sandbox/src/interception_agent.cc deleted file mode 100644 index b40364f..0000000 --- a/sandbox/src/interception_agent.cc +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// For information about interceptions as a whole see -// http://dev.chromium.org/developers/design-documents/sandbox . - -#include "sandbox/src/interception_agent.h" - -#include "sandbox/src/interception_internal.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/eat_resolver.h" -#include "sandbox/src/sidestep_resolver.h" -#include "sandbox/src/sandbox_nt_util.h" - -namespace { - -// Returns true if target lies between base and base + range. -bool IsWithinRange(const void* base, size_t range, const void* target) { - const char* end = reinterpret_cast(base) + range; - return reinterpret_cast(target) < end; -} - -} // namespace - -namespace sandbox { - -// This is the list of all imported symbols from ntdll.dll. -SANDBOX_INTERCEPT NtExports g_nt; - -// The list of intercepted functions back-pointers. -SANDBOX_INTERCEPT OriginalFunctions g_originals; - -// Memory buffer mapped from the parent, with the list of interceptions. -SANDBOX_INTERCEPT SharedMemory* g_interceptions = NULL; - -InterceptionAgent* InterceptionAgent::GetInterceptionAgent() { - static InterceptionAgent* s_singleton = NULL; - if (!s_singleton) { - if (!g_interceptions) - return NULL; - - size_t array_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*); - s_singleton = reinterpret_cast( - new(NT_ALLOC) char[array_bytes + sizeof(InterceptionAgent)]); - - bool success = s_singleton->Init(g_interceptions); - if (!success) { - operator delete(s_singleton, NT_ALLOC); - s_singleton = NULL; - } - } - return s_singleton; -} - -bool InterceptionAgent::Init(SharedMemory* shared_memory) { - interceptions_ = shared_memory; - for (int i = 0 ; i < shared_memory->num_intercepted_dlls; i++) - dlls_[i] = NULL; - return true; -} - -bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path, - const UNICODE_STRING* name, - const DllPatchInfo* dll_info) { - UNICODE_STRING current_name; - current_name.Length = static_cast(g_nt.wcslen(dll_info->dll_name) * - sizeof(wchar_t)); - current_name.MaximumLength = current_name.Length; - current_name.Buffer = const_cast(dll_info->dll_name); - - BOOLEAN case_insensitive = TRUE; - if (full_path && - !g_nt.RtlCompareUnicodeString(¤t_name, full_path, case_insensitive)) - return true; - - if (name && - !g_nt.RtlCompareUnicodeString(¤t_name, name, case_insensitive)) - return true; - - return false; -} - -bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, - const UNICODE_STRING* name, - void* base_address) { - DllPatchInfo* dll_info = interceptions_->dll_list; - int i = 0; - for (; i < interceptions_->num_intercepted_dlls; i++) { - if (DllMatch(full_path, name, dll_info)) - break; - - dll_info = reinterpret_cast( - reinterpret_cast(dll_info) + dll_info->record_bytes); - } - - // Return now if the dll is not in our list of interest. - if (i == interceptions_->num_intercepted_dlls) - return true; - - // The dll must be unloaded. - if (dll_info->unload_module) - return false; - - // Purify causes this condition to trigger. - if (dlls_[i]) - return true; - - size_t buffer_bytes = offsetof(DllInterceptionData, thunks) + - dll_info->num_functions * sizeof(ThunkData); - dlls_[i] = reinterpret_cast( - new(NT_PAGE, base_address) char[buffer_bytes]); - - DCHECK_NT(dlls_[i]); - if (!dlls_[i]) - return true; - - dlls_[i]->data_bytes = buffer_bytes; - dlls_[i]->num_thunks = 0; - dlls_[i]->base = base_address; - dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks); - - VERIFY(PatchDll(dll_info, dlls_[i])); - - ULONG old_protect; - SIZE_T real_size = buffer_bytes; - void* to_protect = dlls_[i]; - VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect, - &real_size, PAGE_EXECUTE_READ, - &old_protect)); - return true; -} - -void InterceptionAgent::OnDllUnload(void* base_address) { - for (int i = 0; i < interceptions_->num_intercepted_dlls; i++) { - if (dlls_[i] && dlls_[i]->base == base_address) { - operator delete(dlls_[i], NT_PAGE); - dlls_[i] = NULL; - break; - } - } -} - -// TODO(rvargas): We have to deal with prebinded dlls. I see two options: change -// the timestamp of the patched dll, or modify the info on the prebinded dll. -// the first approach messes matching of debug symbols, the second one is more -// complicated. -bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info, - DllInterceptionData* thunks) { - DCHECK_NT(NULL != thunks); - DCHECK_NT(NULL != dll_info); - - const FunctionInfo* function = reinterpret_cast( - reinterpret_cast(dll_info) + dll_info->offset_to_functions); - - for (int i = 0; i < dll_info->num_functions; i++) { - if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) { - NOTREACHED_NT(); - return false; - } - - ResolverThunk* resolver = GetResolver(function->type); - if (!resolver) - return false; - - const char* interceptor = function->function + - g_nt.strlen(function->function) + 1; - - if (!IsWithinRange(function, function->record_bytes, interceptor) || - !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) { - NOTREACHED_NT(); - return false; - } - - NTSTATUS ret = resolver->Setup(thunks->base, - interceptions_->interceptor_base, - function->function, - interceptor, - function->interceptor_address, - &thunks->thunks[i], - sizeof(ThunkData), - NULL); - if (!NT_SUCCESS(ret)) { - NOTREACHED_NT(); - return false; - } - - DCHECK_NT(!g_originals[function->id]); - g_originals[function->id] = &thunks->thunks[i]; - - thunks->num_thunks++; - thunks->used_bytes += sizeof(ThunkData); - - function = reinterpret_cast( - reinterpret_cast(function) + function->record_bytes); - } - - return true; -} - -// This method is called from within the loader lock -ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) { - static EatResolverThunk* eat_resolver = NULL; - static SidestepResolverThunk* sidestep_resolver = NULL; - static SmartSidestepResolverThunk* smart_sidestep_resolver = NULL; - - if (!eat_resolver) - eat_resolver = new(NT_ALLOC) EatResolverThunk; - -#if !defined(_WIN64) - // Sidestep is not supported for x64. - if (!sidestep_resolver) - sidestep_resolver = new(NT_ALLOC) SidestepResolverThunk; - - if (!smart_sidestep_resolver) - smart_sidestep_resolver = new(NT_ALLOC) SmartSidestepResolverThunk; -#endif - - switch (type) { - case INTERCEPTION_EAT: - return eat_resolver; - case INTERCEPTION_SIDESTEP: - return sidestep_resolver; - case INTERCEPTION_SMART_SIDESTEP: - return smart_sidestep_resolver; - default: - NOTREACHED_NT(); - } - - return NULL; -} - -} // namespace sandbox diff --git a/sandbox/src/interception_agent.h b/sandbox/src/interception_agent.h deleted file mode 100644 index 10679dd..0000000 --- a/sandbox/src/interception_agent.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Defines InterceptionAgent, the class in charge of setting up interceptions -// from the inside of the sandboxed process. For more details see -// http://dev.chromium.org/developers/design-documents/sandbox . - -#ifndef SANDBOX_SRC_INTERCEPTION_AGENT_H__ -#define SANDBOX_SRC_INTERCEPTION_AGENT_H__ - -#include "base/basictypes.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -namespace sandbox { - -// Internal structures used for communication between the broker and the target. -struct DllInterceptionData; -struct SharedMemory; -struct DllPatchInfo; - -class ResolverThunk; - -// The InterceptionAgent executes on the target application, and it is in charge -// of setting up the desired interceptions or indicating what module needs to -// be unloaded. -// -// The exposed API consists of three methods: GetInterceptionAgent to retrieve -// the single class instance, OnDllLoad and OnDllUnload to process a dll being -// loaded and unloaded respectively. -// -// This class assumes that it will get called for every dll being loaded, -// starting with kernel32, so the singleton will be instantiated from within the -// loader lock. -class InterceptionAgent { - public: - // Returns the single InterceptionAgent object for this process. - static InterceptionAgent* GetInterceptionAgent(); - - // This method should be invoked whenever a new dll is loaded to perform the - // required patches. If the return value is false, this dll should not be - // allowed to load. - // - // full_path is the (optional) full name of the module being loaded and name - // is the internal module name. If full_path is provided, it will be used - // before the internal name to determine if we care about this dll. - bool OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name, - void* base_address); - - // Performs cleanup when a dll is unloaded. - void OnDllUnload(void* base_address); - - private: - ~InterceptionAgent() {} - - // Performs initialization of the singleton. - bool Init(SharedMemory* shared_memory); - - // Returns true if we are interested on this dll. dll_info is an entry of the - // list of intercepted dlls. - bool DllMatch(const UNICODE_STRING* full_path, const UNICODE_STRING* name, - const DllPatchInfo* dll_info); - - // Performs the patching of the dll loaded at base_address. - // The patches to perform are described on dll_info, and thunks is the thunk - // storage for the whole dll. - // Returns true on success. - bool PatchDll(const DllPatchInfo* dll_info, DllInterceptionData* thunks); - - // Returns a resolver for a given interception type. - ResolverThunk* GetResolver(InterceptionType type); - - // Shared memory containing the list of functions to intercept. - SharedMemory* interceptions_; - - // Array of thunk data buffers for the intercepted dlls. This object singleton - // is allocated with a placement new with enough space to hold the complete - // array of pointers, not just the first element. - DllInterceptionData* dlls_[1]; - - DISALLOW_IMPLICIT_CONSTRUCTORS(InterceptionAgent); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_INTERCEPTION_AGENT_H__ diff --git a/sandbox/src/interception_internal.h b/sandbox/src/interception_internal.h deleted file mode 100644 index f3c401c..0000000 --- a/sandbox/src/interception_internal.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Defines InterceptionManager, the class in charge of setting up interceptions -// for the sandboxed process. For more details see: -// http://dev.chromium.org/developers/design-documents/sandbox . - -#ifndef SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ -#define SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ - -#include "sandbox/src/sandbox_types.h" - -namespace sandbox { - -const int kMaxThunkDataBytes = 64; - -enum InterceptorId; - -// The following structures contain variable size fields at the end, and will be -// used to transfer information between two processes. In order to guarantee -// our ability to follow the chain of structures, the alignment should be fixed, -// hence this pragma. -#pragma pack(push, 4) - -// Structures for the shared memory that contains patching information -// for the InterceptionAgent. -// A single interception: -struct FunctionInfo { - size_t record_bytes; // rounded to sizeof(size_t) bytes - InterceptionType type; - InterceptorId id; - const void* interceptor_address; - char function[1]; // placeholder for null terminated name - // char interceptor[] // followed by the interceptor function -}; - -// A single dll: -struct DllPatchInfo { - size_t record_bytes; // rounded to sizeof(size_t) bytes - size_t offset_to_functions; - int num_functions; - bool unload_module; - wchar_t dll_name[1]; // placeholder for null terminated name - // FunctionInfo function_info[] // followed by the functions to intercept -}; - -// All interceptions: -struct SharedMemory { - int num_intercepted_dlls; - void* interceptor_base; - DllPatchInfo dll_list[1]; // placeholder for the list of dlls -}; - -// Dummy single thunk: -struct ThunkData { - char data[kMaxThunkDataBytes]; -}; - -// In-memory representation of the interceptions for a given dll: -struct DllInterceptionData { - size_t data_bytes; - size_t used_bytes; - void* base; - int num_thunks; -#if defined(_WIN64) - int dummy; // Improve alignment. -#endif - ThunkData thunks[1]; -}; - -#pragma pack(pop) - -} // namespace sandbox - -#endif // SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ diff --git a/sandbox/src/interception_unittest.cc b/sandbox/src/interception_unittest.cc deleted file mode 100644 index a0dd98d..0000000 --- a/sandbox/src/interception_unittest.cc +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains unit tests for InterceptionManager. -// The tests require private information so the whole interception.cc file is -// included from this file. - -#include - -#include "base/memory/scoped_ptr.h" -#include "sandbox/src/interception.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/interception_internal.h" -#include "sandbox/src/target_process.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -// Walks the settings buffer, verifying that the values make sense and counting -// objects. -// Arguments: -// buffer (in): the buffer to walk. -// size (in): buffer size -// num_dlls (out): count of the dlls on the buffer. -// num_function (out): count of intercepted functions. -// num_names (out): count of named interceptor functions. -void WalkBuffer(void* buffer, size_t size, int* num_dlls, int* num_functions, - int* num_names) { - ASSERT_TRUE(NULL != buffer); - ASSERT_TRUE(NULL != num_functions); - ASSERT_TRUE(NULL != num_names); - *num_dlls = *num_functions = *num_names = 0; - SharedMemory *memory = reinterpret_cast(buffer); - - ASSERT_GT(size, sizeof(SharedMemory)); - DllPatchInfo *dll = &memory->dll_list[0]; - - for (int i = 0; i < memory->num_intercepted_dlls; i++) { - ASSERT_NE(0u, wcslen(dll->dll_name)); - ASSERT_EQ(0u, dll->record_bytes % sizeof(size_t)); - ASSERT_EQ(0u, dll->offset_to_functions % sizeof(size_t)); - ASSERT_NE(0, dll->num_functions); - - FunctionInfo *function = reinterpret_cast( - reinterpret_cast(dll) + dll->offset_to_functions); - - for (int j = 0; j < dll->num_functions; j++) { - ASSERT_EQ(0u, function->record_bytes % sizeof(size_t)); - - char* name = function->function; - size_t length = strlen(name); - ASSERT_NE(0u, length); - name += length + 1; - - // look for overflows - ASSERT_GT(reinterpret_cast(buffer) + size, name + strlen(name)); - - // look for a named interceptor - if (strlen(name)) { - (*num_names)++; - EXPECT_TRUE(NULL == function->interceptor_address); - } else { - EXPECT_TRUE(NULL != function->interceptor_address); - } - - (*num_functions)++; - function = reinterpret_cast( - reinterpret_cast(function) + function->record_bytes); - } - - (*num_dlls)++; - dll = reinterpret_cast(reinterpret_cast(dll) + - dll->record_bytes); - } -} - -TEST(InterceptionManagerTest, BufferLayout1) { - wchar_t exe_name[MAX_PATH]; - ASSERT_NE(0u, GetModuleFileName(NULL, exe_name, MAX_PATH - 1)); - - TargetProcess *target = MakeTestTargetProcess(::GetCurrentProcess(), - ::GetModuleHandle(exe_name)); - - InterceptionManager interceptions(target, true); - - // Any pointer will do for a function pointer. - void* function = &interceptions; - - // We don't care about the interceptor id. - interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile", - INTERCEPTION_SERVICE_CALL, function, - OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx", - INTERCEPTION_SMART_SIDESTEP, function, - OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"user32.dll", "FindWindow", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateMutex", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg", - INTERCEPTION_EAT, "replacement", - OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtClose", - INTERCEPTION_SERVICE_CALL, function, - OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtOpenFile", - INTERCEPTION_SIDESTEP, function, - OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"some.dll", "Superfn", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", - INTERCEPTION_EAT, "a", OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", - INTERCEPTION_SIDESTEP, "ab", OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", - INTERCEPTION_EAT, "abc", OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"a.dll", "p", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"b.dll", - "TheIncredibleCallToSaveTheWorld", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"a.dll", "BIsLame", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - interceptions.AddToPatchedFunctions(L"a.dll", "ARules", - INTERCEPTION_EAT, function, OPEN_KEY_ID); - - // Verify that all interceptions were added - ASSERT_EQ(18, interceptions.interceptions_.size()); - - size_t buffer_size = interceptions.GetBufferSize(); - scoped_array local_buffer(new BYTE[buffer_size]); - - ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), - buffer_size)); - - // At this point, the interceptions should have been separated into two - // groups: one group with the local ("cold") interceptions, consisting of - // everything from ntdll and stuff set as INTRECEPTION_SERVICE_CALL, and - // another group with the interceptions belonging to dlls that will be "hot" - // patched on the client. The second group lives on local_buffer, and the - // first group remains on the list of interceptions (inside the object - // "interceptions"). There are 3 local interceptions (of ntdll); the - // other 15 have to be sent to the child to be performed "hot". - EXPECT_EQ(3, interceptions.interceptions_.size()); - - int num_dlls, num_functions, num_names; - WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions, - &num_names); - - // The 15 interceptions on the buffer (to the child) should be grouped on 6 - // dlls. Only four interceptions are using an explicit name for the - // interceptor function. - EXPECT_EQ(6, num_dlls); - EXPECT_EQ(15, num_functions); - EXPECT_EQ(4, num_names); -} - -TEST(InterceptionManagerTest, BufferLayout2) { - wchar_t exe_name[MAX_PATH]; - ASSERT_NE(0u, GetModuleFileName(NULL, exe_name, MAX_PATH - 1)); - - TargetProcess *target = MakeTestTargetProcess(::GetCurrentProcess(), - ::GetModuleHandle(exe_name)); - - InterceptionManager interceptions(target, true); - - // Any pointer will do for a function pointer. - void* function = &interceptions; - interceptions.AddToUnloadModules(L"some01.dll"); - // We don't care about the interceptor id. - interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile", - INTERCEPTION_SERVICE_CALL, function, - OPEN_FILE_ID); - interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx", - INTERCEPTION_EAT, function, OPEN_FILE_ID); - interceptions.AddToUnloadModules(L"some02.dll"); - interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx", - INTERCEPTION_SMART_SIDESTEP, function, - OPEN_FILE_ID); - // Verify that all interceptions were added - ASSERT_EQ(5, interceptions.interceptions_.size()); - - size_t buffer_size = interceptions.GetBufferSize(); - scoped_array local_buffer(new BYTE[buffer_size]); - - ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), - buffer_size)); - - // At this point, the interceptions should have been separated into two - // groups: one group with the local ("cold") interceptions, and another - // group with the interceptions belonging to dlls that will be "hot" - // patched on the client. The second group lives on local_buffer, and the - // first group remains on the list of interceptions, in this case just one. - EXPECT_EQ(1, interceptions.interceptions_.size()); - - int num_dlls, num_functions, num_names; - WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions, - &num_names); - - EXPECT_EQ(3, num_dlls); - EXPECT_EQ(4, num_functions); - EXPECT_EQ(0, num_names); -} - -} // namespace sandbox diff --git a/sandbox/src/interceptors.h b/sandbox/src/interceptors.h deleted file mode 100644 index 67b0900..0000000 --- a/sandbox/src/interceptors.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_INTERCEPTORS_H_ -#define SANDBOX_SRC_INTERCEPTORS_H_ - -#if defined(_WIN64) -#include "sandbox/src/interceptors_64.h" -#endif - -namespace sandbox { - -enum InterceptorId { - // Internal use: - MAP_VIEW_OF_SECTION_ID = 0, - UNMAP_VIEW_OF_SECTION_ID, - // Policy broker: - SET_INFORMATION_THREAD_ID, - OPEN_THREAD_TOKEN_ID, - OPEN_THREAD_TOKEN_EX_ID, - OPEN_TREAD_ID, - OPEN_PROCESS_ID, - OPEN_PROCESS_TOKEN_ID, - OPEN_PROCESS_TOKEN_EX_ID, - // Filesystem dispatcher: - CREATE_FILE_ID, - OPEN_FILE_ID, - QUERY_ATTRIB_FILE_ID, - QUERY_FULL_ATTRIB_FILE_ID, - SET_INFO_FILE_ID, - // Named pipe dispatcher: - CREATE_NAMED_PIPE_ID, - // Process-thread dispatcher: - CREATE_PROCESSW_ID, - CREATE_PROCESSA_ID, - // Registry dispatcher: - CREATE_KEY_ID, - OPEN_KEY_ID, - OPEN_KEY_EX_ID, - // Sync dispatcher: - CREATE_EVENT_ID, - OPEN_EVENT_ID, - // CSRSS bypasses for HandleCloser: - CREATE_THREAD_ID, - GET_USER_DEFAULT_LCID_ID, - INTERCEPTOR_MAX_ID -}; - -typedef void* OriginalFunctions[INTERCEPTOR_MAX_ID]; - -} // namespace sandbox - -#endif // SANDBOX_SRC_INTERCEPTORS_H_ diff --git a/sandbox/src/interceptors_64.cc b/sandbox/src/interceptors_64.cc deleted file mode 100644 index c068010..0000000 --- a/sandbox/src/interceptors_64.cc +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/interceptors_64.h" - -#include "sandbox/src/interceptors.h" -#include "sandbox/src/filesystem_interception.h" -#include "sandbox/src/named_pipe_interception.h" -#include "sandbox/src/policy_target.h" -#include "sandbox/src/process_thread_interception.h" -#include "sandbox/src/registry_interception.h" -#include "sandbox/src/sandbox_nt_types.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/sync_interception.h" -#include "sandbox/src/target_interceptions.h" - -namespace sandbox { - -SANDBOX_INTERCEPT NtExports g_nt; -SANDBOX_INTERCEPT OriginalFunctions g_originals; - -NTSTATUS WINAPI TargetNtMapViewOfSection64( - HANDLE section, HANDLE process, PVOID *base, ULONG_PTR zero_bits, - SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size, - SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect) { - NtMapViewOfSectionFunction orig_fn = reinterpret_cast< - NtMapViewOfSectionFunction>(g_originals[MAP_VIEW_OF_SECTION_ID]); - - return TargetNtMapViewOfSection(orig_fn, section, process, base, zero_bits, - commit_size, offset, view_size, inherit, - allocation_type, protect); -} - -NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process, PVOID base) { - NtUnmapViewOfSectionFunction orig_fn = reinterpret_cast< - NtUnmapViewOfSectionFunction>(g_originals[UNMAP_VIEW_OF_SECTION_ID]); - return TargetNtUnmapViewOfSection(orig_fn, process, base); -} - -// ----------------------------------------------------------------------- - -NTSTATUS WINAPI TargetNtSetInformationThread64( - HANDLE thread, NT_THREAD_INFORMATION_CLASS thread_info_class, - PVOID thread_information, ULONG thread_information_bytes) { - NtSetInformationThreadFunction orig_fn = reinterpret_cast< - NtSetInformationThreadFunction>(g_originals[SET_INFORMATION_THREAD_ID]); - return TargetNtSetInformationThread(orig_fn, thread, thread_info_class, - thread_information, - thread_information_bytes); -} - -NTSTATUS WINAPI TargetNtOpenThreadToken64( - HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self, - PHANDLE token) { - NtOpenThreadTokenFunction orig_fn = reinterpret_cast< - NtOpenThreadTokenFunction>(g_originals[OPEN_THREAD_TOKEN_ID]); - return TargetNtOpenThreadToken(orig_fn, thread, desired_access, open_as_self, - token); -} - -NTSTATUS WINAPI TargetNtOpenThreadTokenEx64( - HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self, - ULONG handle_attributes, PHANDLE token) { - NtOpenThreadTokenExFunction orig_fn = reinterpret_cast< - NtOpenThreadTokenExFunction>(g_originals[OPEN_THREAD_TOKEN_EX_ID]); - return TargetNtOpenThreadTokenEx(orig_fn, thread, desired_access, - open_as_self, handle_attributes, token); -} - -HANDLE WINAPI TargetCreateThread64( - LPSECURITY_ATTRIBUTES thread_attributes, SIZE_T stack_size, - LPTHREAD_START_ROUTINE start_address, PVOID parameter, DWORD creation_flags, - LPDWORD thread_id) { - CreateThreadFunction orig_fn = reinterpret_cast< - CreateThreadFunction>(g_originals[CREATE_THREAD_ID]); - return TargetCreateThread(orig_fn, thread_attributes, stack_size, - start_address, parameter, creation_flags, - thread_id); -} - -LCID WINAPI TargetGetUserDefaultLCID64(void) { - GetUserDefaultLCIDFunction orig_fn = reinterpret_cast< - GetUserDefaultLCIDFunction>(g_originals[GET_USER_DEFAULT_LCID_ID]); - return TargetGetUserDefaultLCID(orig_fn); -} - -// ----------------------------------------------------------------------- - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateFile64( - PHANDLE file, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, - PLARGE_INTEGER allocation_size, ULONG file_attributes, ULONG sharing, - ULONG disposition, ULONG options, PVOID ea_buffer, ULONG ea_length) { - NtCreateFileFunction orig_fn = reinterpret_cast< - NtCreateFileFunction>(g_originals[CREATE_FILE_ID]); - return TargetNtCreateFile(orig_fn, file, desired_access, object_attributes, - io_status, allocation_size, file_attributes, - sharing, disposition, options, ea_buffer, - ea_length); -} - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenFile64( - PHANDLE file, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, - ULONG sharing, ULONG options) { - NtOpenFileFunction orig_fn = reinterpret_cast< - NtOpenFileFunction>(g_originals[OPEN_FILE_ID]); - return TargetNtOpenFile(orig_fn, file, desired_access, object_attributes, - io_status, sharing, options); -} - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryAttributesFile64( - POBJECT_ATTRIBUTES object_attributes, - PFILE_BASIC_INFORMATION file_attributes) { - NtQueryAttributesFileFunction orig_fn = reinterpret_cast< - NtQueryAttributesFileFunction>(g_originals[QUERY_ATTRIB_FILE_ID]); - return TargetNtQueryAttributesFile(orig_fn, object_attributes, - file_attributes); -} - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64( - POBJECT_ATTRIBUTES object_attributes, - PFILE_NETWORK_OPEN_INFORMATION file_attributes) { - NtQueryFullAttributesFileFunction orig_fn = reinterpret_cast< - NtQueryFullAttributesFileFunction>( - g_originals[QUERY_FULL_ATTRIB_FILE_ID]); - return TargetNtQueryFullAttributesFile(orig_fn, object_attributes, - file_attributes); -} - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationFile64( - HANDLE file, PIO_STATUS_BLOCK io_status, PVOID file_information, - ULONG length, FILE_INFORMATION_CLASS file_information_class) { - NtSetInformationFileFunction orig_fn = reinterpret_cast< - NtSetInformationFileFunction>(g_originals[SET_INFO_FILE_ID]); - return TargetNtSetInformationFile(orig_fn, file, io_status, file_information, - length, file_information_class); -} - -// ----------------------------------------------------------------------- - -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateNamedPipeW64( - LPCWSTR pipe_name, DWORD open_mode, DWORD pipe_mode, DWORD max_instance, - DWORD out_buffer_size, DWORD in_buffer_size, DWORD default_timeout, - LPSECURITY_ATTRIBUTES security_attributes) { - CreateNamedPipeWFunction orig_fn = reinterpret_cast< - CreateNamedPipeWFunction>(g_originals[CREATE_NAMED_PIPE_ID]); - return TargetCreateNamedPipeW(orig_fn, pipe_name, open_mode, pipe_mode, - max_instance, out_buffer_size, in_buffer_size, - default_timeout, security_attributes); -} - -// ----------------------------------------------------------------------- - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThread64( - PHANDLE thread, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id) { - NtOpenThreadFunction orig_fn = reinterpret_cast< - NtOpenThreadFunction>(g_originals[OPEN_TREAD_ID]); - return TargetNtOpenThread(orig_fn, thread, desired_access, object_attributes, - client_id); -} - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcess64( - PHANDLE process, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id) { - NtOpenProcessFunction orig_fn = reinterpret_cast< - NtOpenProcessFunction>(g_originals[OPEN_PROCESS_ID]); - return TargetNtOpenProcess(orig_fn, process, desired_access, - object_attributes, client_id); -} - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessToken64( - HANDLE process, ACCESS_MASK desired_access, PHANDLE token) { - NtOpenProcessTokenFunction orig_fn = reinterpret_cast< - NtOpenProcessTokenFunction>(g_originals[OPEN_PROCESS_TOKEN_ID]); - return TargetNtOpenProcessToken(orig_fn, process, desired_access, token); -} - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessTokenEx64( - HANDLE process, ACCESS_MASK desired_access, ULONG handle_attributes, - PHANDLE token) { - NtOpenProcessTokenExFunction orig_fn = reinterpret_cast< - NtOpenProcessTokenExFunction>(g_originals[OPEN_PROCESS_TOKEN_EX_ID]); - return TargetNtOpenProcessTokenEx(orig_fn, process, desired_access, - handle_attributes, token); -} - -SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessW64( - LPCWSTR application_name, LPWSTR command_line, - LPSECURITY_ATTRIBUTES process_attributes, - LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, - LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info, - LPPROCESS_INFORMATION process_information) { - CreateProcessWFunction orig_fn = reinterpret_cast< - CreateProcessWFunction>(g_originals[CREATE_PROCESSW_ID]); - return TargetCreateProcessW(orig_fn, application_name, command_line, - process_attributes, thread_attributes, - inherit_handles, flags, environment, - current_directory, startup_info, - process_information); -} - -SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA64( - LPCSTR application_name, LPSTR command_line, - LPSECURITY_ATTRIBUTES process_attributes, - LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, - LPVOID environment, LPCSTR current_directory, LPSTARTUPINFOA startup_info, - LPPROCESS_INFORMATION process_information) { - CreateProcessAFunction orig_fn = reinterpret_cast< - CreateProcessAFunction>(g_originals[CREATE_PROCESSA_ID]); - return TargetCreateProcessA(orig_fn, application_name, command_line, - process_attributes, thread_attributes, - inherit_handles, flags, environment, - current_directory, startup_info, - process_information); -} - -// ----------------------------------------------------------------------- - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey64( - PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, ULONG title_index, - PUNICODE_STRING class_name, ULONG create_options, PULONG disposition) { - NtCreateKeyFunction orig_fn = reinterpret_cast< - NtCreateKeyFunction>(g_originals[CREATE_KEY_ID]); - return TargetNtCreateKey(orig_fn, key, desired_access, object_attributes, - title_index, class_name, create_options, - disposition); -} - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey64( - PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes) { - NtOpenKeyFunction orig_fn = reinterpret_cast< - NtOpenKeyFunction>(g_originals[OPEN_KEY_ID]); - return TargetNtOpenKey(orig_fn, key, desired_access, object_attributes); -} - -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx64( - PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, ULONG open_options) { - NtOpenKeyExFunction orig_fn = reinterpret_cast< - NtOpenKeyExFunction>(g_originals[OPEN_KEY_EX_ID]); - return TargetNtOpenKeyEx(orig_fn, key, desired_access, object_attributes, - open_options); -} - -// ----------------------------------------------------------------------- - -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW64( - LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset, - BOOL initial_state, LPCWSTR name) { - CreateEventWFunction orig_fn = reinterpret_cast< - CreateEventWFunction>(g_originals[CREATE_EVENT_ID]); - return TargetCreateEventW(orig_fn, security_attributes, manual_reset, - initial_state, name); -} - -SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW64( - ACCESS_MASK desired_access, BOOL inherit_handle, LPCWSTR name) { - OpenEventWFunction orig_fn = reinterpret_cast< - OpenEventWFunction>(g_originals[OPEN_EVENT_ID]); - return TargetOpenEventW(orig_fn, desired_access, inherit_handle, name); -} - -} // namespace sandbox diff --git a/sandbox/src/interceptors_64.h b/sandbox/src/interceptors_64.h deleted file mode 100644 index 50355a0..0000000 --- a/sandbox/src/interceptors_64.h +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -#ifndef SANDBOX_SRC_INTERCEPTORS_64_H_ -#define SANDBOX_SRC_INTERCEPTORS_64_H_ - -namespace sandbox { - -extern "C" { - -// Interception of NtMapViewOfSection on the child process. -// It should never be called directly. This function provides the means to -// detect dlls being loaded, so we can patch them if needed. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtMapViewOfSection64( - HANDLE section, HANDLE process, PVOID *base, ULONG_PTR zero_bits, - SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size, - SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect); - -// Interception of NtUnmapViewOfSection on the child process. -// It should never be called directly. This function provides the means to -// detect dlls being unloaded, so we can clean up our interceptions. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process, - PVOID base); - -// ----------------------------------------------------------------------- -// Interceptors without IPC. - -// Interception of NtSetInformationThread on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationThread64( - HANDLE thread, NT_THREAD_INFORMATION_CLASS thread_info_class, - PVOID thread_information, ULONG thread_information_bytes); - -// Interception of NtOpenThreadToken on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadToken64( - HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self, - PHANDLE token); - -// Interception of NtOpenThreadTokenEx on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadTokenEx64( - HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self, - ULONG handle_attributes, PHANDLE token); - -// Interception of CreateThread on the child process. -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateThread64( - LPSECURITY_ATTRIBUTES thread_attributes, SIZE_T stack_size, - LPTHREAD_START_ROUTINE start_address, PVOID parameter, - DWORD creation_flags, LPDWORD thread_id); - -// Interception of GetUserDefaultLCID on the child process. -SANDBOX_INTERCEPT LCID WINAPI TargetGetUserDefaultLCID64(); - -// ----------------------------------------------------------------------- -// Interceptors handled by the file system dispatcher. - -// Interception of NtCreateFile on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateFile64( - PHANDLE file, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, - PLARGE_INTEGER allocation_size, ULONG file_attributes, ULONG sharing, - ULONG disposition, ULONG options, PVOID ea_buffer, ULONG ea_length); - -// Interception of NtOpenFile on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenFile64( - PHANDLE file, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, - ULONG sharing, ULONG options); - -// Interception of NtQueryAtttributesFile on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryAttributesFile64( - POBJECT_ATTRIBUTES object_attributes, - PFILE_BASIC_INFORMATION file_attributes); - -// Interception of NtQueryFullAtttributesFile on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64( - POBJECT_ATTRIBUTES object_attributes, - PFILE_NETWORK_OPEN_INFORMATION file_attributes); - -// Interception of NtSetInformationFile on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationFile64( - HANDLE file, PIO_STATUS_BLOCK io_status, PVOID file_information, - ULONG length, FILE_INFORMATION_CLASS file_information_class); - -// ----------------------------------------------------------------------- -// Interceptors handled by the named pipe dispatcher. - -// Interception of CreateNamedPipeW in kernel32.dll -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateNamedPipeW64( - LPCWSTR pipe_name, DWORD open_mode, DWORD pipe_mode, DWORD max_instance, - DWORD out_buffer_size, DWORD in_buffer_size, DWORD default_timeout, - LPSECURITY_ATTRIBUTES security_attributes); - -// ----------------------------------------------------------------------- -// Interceptors handled by the process-thread dispatcher. - -// Interception of NtOpenThread on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThread64( - PHANDLE thread, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id); - -// Interception of NtOpenProcess on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcess64( - PHANDLE process, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id); - -// Interception of NtOpenProcessToken on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessToken64( - HANDLE process, ACCESS_MASK desired_access, PHANDLE token); - -// Interception of NtOpenProcessTokenEx on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessTokenEx64( - HANDLE process, ACCESS_MASK desired_access, ULONG handle_attributes, - PHANDLE token); - -// Interception of CreateProcessW in kernel32.dll. -SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessW64( - LPCWSTR application_name, LPWSTR command_line, - LPSECURITY_ATTRIBUTES process_attributes, - LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, - LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info, - LPPROCESS_INFORMATION process_information); - -// Interception of CreateProcessA in kernel32.dll. -SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA64( - LPCSTR application_name, LPSTR command_line, - LPSECURITY_ATTRIBUTES process_attributes, - LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, - LPVOID environment, LPCSTR current_directory, LPSTARTUPINFOA startup_info, - LPPROCESS_INFORMATION process_information); - -// ----------------------------------------------------------------------- -// Interceptors handled by the registry dispatcher. - -// Interception of NtCreateKey on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey64( - PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, ULONG title_index, - PUNICODE_STRING class_name, ULONG create_options, PULONG disposition); - -// Interception of NtOpenKey on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey64( - PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes); - -// Interception of NtOpenKeyEx on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx64( - PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, ULONG open_options); - -// ----------------------------------------------------------------------- -// Interceptors handled by the sync dispatcher. - -// Interception of CreateEventW on the child process. -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW64( - LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset, - BOOL initial_state, LPCWSTR name); - -// Interception of OpenEventW on the child process. -SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW64( - ACCESS_MASK desired_access, BOOL inherit_handle, LPCWSTR name); - -} // extern "C" - -} // namespace sandbox - -#endif // SANDBOX_SRC_INTERCEPTORS_64_H_ diff --git a/sandbox/src/internal_types.h b/sandbox/src/internal_types.h deleted file mode 100644 index db969aa..0000000 --- a/sandbox/src/internal_types.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_INTERNAL_TYPES_H_ -#define SANDBOX_SRC_INTERNAL_TYPES_H_ - -namespace sandbox { - -const wchar_t kNtdllName[] = L"ntdll.dll"; -const wchar_t kKerneldllName[] = L"kernel32.dll"; - -// Defines the supported C++ types encoding to numeric id. Like a poor's man -// RTTI. Note that true C++ RTTI will not work because the types are not -// polymorphic anyway. -enum ArgType { - INVALID_TYPE = 0, - WCHAR_TYPE, - ULONG_TYPE, - UNISTR_TYPE, - VOIDPTR_TYPE, - INPTR_TYPE, - INOUTPTR_TYPE, - LAST_TYPE -}; - -// Encapsulates a pointer to a buffer and the size of the buffer. -class CountedBuffer { - public: - CountedBuffer(void* buffer, uint32 size) : size_(size), buffer_(buffer) {} - - uint32 Size() const { - return size_; - } - - void* Buffer() const { - return buffer_; - } - - private: - uint32 size_; - void* buffer_; -}; - -// Helper class to convert void-pointer packed ints for both -// 32 and 64 bit builds. This construct is non-portable. -class IPCInt { - public: - explicit IPCInt(void* buffer) { - buffer_.vp = buffer; - } - - explicit IPCInt(unsigned __int32 i32) { - buffer_.vp = NULL; - buffer_.i32 = i32; - } - - unsigned __int32 As32Bit() const { - return buffer_.i32; - } - - void* AsVoidPtr() const { - return buffer_.vp; - } - - private: - union U { - void* vp; - unsigned __int32 i32; - } buffer_; -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_INTERNAL_TYPES_H_ diff --git a/sandbox/src/ipc_ping_test.cc b/sandbox/src/ipc_ping_test.cc deleted file mode 100644 index 410fd6a..0000000 --- a/sandbox/src/ipc_ping_test.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/target_services.h" -#include "sandbox/tests/common/controller.h" - -namespace sandbox { - -// Tests that the IPC is working by issuing a special IPC that is not exposed -// in the public API. -SBOX_TESTS_COMMAND int IPC_Ping(int argc, wchar_t **argv) { - if (argc != 1) - return SBOX_TEST_FAILED; - - TargetServices* ts = SandboxFactory::GetTargetServices(); - if (NULL == ts) - return SBOX_TEST_FAILED; - - // Downcast because we have internal knowledge of the object returned. - TargetServicesBase* ts_base = reinterpret_cast(ts); - - int version = 0; - if (L'1' == argv[0][0]) - version = 1; - else - version = 2; - - if (!ts_base->TestIPCPing(version)) - return SBOX_TEST_FAILED; - - ::Sleep(1); - if (!ts_base->TestIPCPing(version)) - return SBOX_TEST_FAILED; - - return SBOX_TEST_SUCCEEDED; -} - -// The IPC ping test should work before and after the token drop. -TEST(IPCTest, IPCPingTestSimple) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(EVERY_STATE); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 1")); -} - -TEST(IPCTest, IPCPingTestWithOutput) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(EVERY_STATE); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2")); -} - -} // namespace sandbox diff --git a/sandbox/src/ipc_tags.h b/sandbox/src/ipc_tags.h deleted file mode 100644 index 4e3a806..0000000 --- a/sandbox/src/ipc_tags.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_IPC_TAGS_H__ -#define SANDBOX_SRC_IPC_TAGS_H__ - -namespace sandbox { - -enum { - IPC_UNUSED_TAG = 0, - IPC_PING1_TAG, // Takes a cookie in parameters and returns the cookie - // multiplied by 2 and the tick_count. Used for testing only. - IPC_PING2_TAG, // Takes an in/out cookie in parameters and modify the cookie - // to be multiplied by 3. Used for testing only. - IPC_NTCREATEFILE_TAG, - IPC_NTOPENFILE_TAG, - IPC_NTQUERYATTRIBUTESFILE_TAG, - IPC_NTQUERYFULLATTRIBUTESFILE_TAG, - IPC_NTSETINFO_RENAME_TAG, - IPC_CREATENAMEDPIPEW_TAG, - IPC_NTOPENTHREAD_TAG, - IPC_NTOPENPROCESS_TAG, - IPC_NTOPENPROCESSTOKEN_TAG, - IPC_NTOPENPROCESSTOKENEX_TAG, - IPC_CREATEPROCESSW_TAG, - IPC_CREATEEVENT_TAG, - IPC_OPENEVENT_TAG, - IPC_NTCREATEKEY_TAG, - IPC_NTOPENKEY_TAG, - IPC_DUPLICATEHANDLEPROXY_TAG, - IPC_LAST_TAG -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_IPC_TAGS_H__ diff --git a/sandbox/src/ipc_unittest.cc b/sandbox/src/ipc_unittest.cc deleted file mode 100644 index e1fb7c1..0000000 --- a/sandbox/src/ipc_unittest.cc +++ /dev/null @@ -1,641 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/sharedmem_ipc_server.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -// Helper function to make the fake shared memory with some -// basic elements initialized. -IPCControl* MakeChannels(size_t channel_size, size_t total_shared_size, - size_t* base_start) { - // Allocate memory - char* mem = new char[total_shared_size]; - memset(mem, 0, total_shared_size); - // Calculate how many channels we can fit in the shared memory. - total_shared_size -= offsetof(IPCControl, channels); - size_t channel_count = - total_shared_size / (sizeof(ChannelControl) + channel_size); - // Calculate the start of the first channel. - *base_start = (sizeof(ChannelControl)* channel_count) + - offsetof(IPCControl, channels); - // Setup client structure. - IPCControl* client_control = reinterpret_cast(mem); - client_control->channels_count = channel_count; - return client_control; -} - -enum TestFixMode { - FIX_NO_EVENTS, - FIX_PONG_READY, - FIX_PONG_NOT_READY -}; - -void FixChannels(IPCControl* client_control, size_t base_start, - size_t channel_size, TestFixMode mode) { - for (size_t ix = 0; ix != client_control->channels_count; ++ix) { - ChannelControl& channel = client_control->channels[ix]; - channel.channel_base = base_start; - channel.state = kFreeChannel; - if (mode != FIX_NO_EVENTS) { - BOOL signaled = (FIX_PONG_READY == mode)? TRUE : FALSE; - channel.ping_event = ::CreateEventW(NULL, FALSE, FALSE, NULL); - channel.pong_event = ::CreateEventW(NULL, FALSE, signaled, NULL); - } - base_start += channel_size; - } -} - -void CloseChannelEvents(IPCControl* client_control) { - for (size_t ix = 0; ix != client_control->channels_count; ++ix) { - ChannelControl& channel = client_control->channels[ix]; - ::CloseHandle(channel.ping_event); - ::CloseHandle(channel.pong_event); - } -} - -TEST(IPCTest, ChannelMaker) { - // Test that our testing rig is computing offsets properly. We should have - // 5 channnels and the offset to the first channel is 108 bytes in 32 bits - // and 216 in 64 bits. - size_t channel_start = 0; - IPCControl* client_control = MakeChannels(12 * 64, 4096, &channel_start); - ASSERT_TRUE(NULL != client_control); - EXPECT_EQ(5, client_control->channels_count); -#if defined(_WIN64) - EXPECT_EQ(216, channel_start); -#else - EXPECT_EQ(108, channel_start); -#endif - delete[] reinterpret_cast(client_control); -} - -TEST(IPCTest, ClientLockUnlock) { - // Make 7 channels of kIPCChannelSize (1kb) each. Test that we lock and - // unlock channels properly. - size_t base_start = 0; - IPCControl* client_control = - MakeChannels(kIPCChannelSize, 4096 * 2, &base_start); - FixChannels(client_control, base_start, kIPCChannelSize, FIX_NO_EVENTS); - - char* mem = reinterpret_cast(client_control); - SharedMemIPCClient client(mem); - - // Test that we lock the first 3 channels in sequence. - void* buff0 = client.GetBuffer(); - EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0); - EXPECT_EQ(kBusyChannel, client_control->channels[0].state); - EXPECT_EQ(kFreeChannel, client_control->channels[1].state); - EXPECT_EQ(kFreeChannel, client_control->channels[2].state); - EXPECT_EQ(kFreeChannel, client_control->channels[3].state); - EXPECT_EQ(kFreeChannel, client_control->channels[4].state); - EXPECT_EQ(kFreeChannel, client_control->channels[5].state); - - void* buff1 = client.GetBuffer(); - EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1); - EXPECT_EQ(kBusyChannel, client_control->channels[0].state); - EXPECT_EQ(kBusyChannel, client_control->channels[1].state); - EXPECT_EQ(kFreeChannel, client_control->channels[2].state); - EXPECT_EQ(kFreeChannel, client_control->channels[3].state); - EXPECT_EQ(kFreeChannel, client_control->channels[4].state); - EXPECT_EQ(kFreeChannel, client_control->channels[5].state); - - void* buff2 = client.GetBuffer(); - EXPECT_TRUE(mem + client_control->channels[2].channel_base == buff2); - EXPECT_EQ(kBusyChannel, client_control->channels[0].state); - EXPECT_EQ(kBusyChannel, client_control->channels[1].state); - EXPECT_EQ(kBusyChannel, client_control->channels[2].state); - EXPECT_EQ(kFreeChannel, client_control->channels[3].state); - EXPECT_EQ(kFreeChannel, client_control->channels[4].state); - EXPECT_EQ(kFreeChannel, client_control->channels[5].state); - - // Test that we unlock and re-lock the right channel. - client.FreeBuffer(buff1); - EXPECT_EQ(kBusyChannel, client_control->channels[0].state); - EXPECT_EQ(kFreeChannel, client_control->channels[1].state); - EXPECT_EQ(kBusyChannel, client_control->channels[2].state); - EXPECT_EQ(kFreeChannel, client_control->channels[3].state); - EXPECT_EQ(kFreeChannel, client_control->channels[4].state); - EXPECT_EQ(kFreeChannel, client_control->channels[5].state); - - void* buff2b = client.GetBuffer(); - EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff2b); - EXPECT_EQ(kBusyChannel, client_control->channels[0].state); - EXPECT_EQ(kBusyChannel, client_control->channels[1].state); - EXPECT_EQ(kBusyChannel, client_control->channels[2].state); - EXPECT_EQ(kFreeChannel, client_control->channels[3].state); - EXPECT_EQ(kFreeChannel, client_control->channels[4].state); - EXPECT_EQ(kFreeChannel, client_control->channels[5].state); - - client.FreeBuffer(buff0); - EXPECT_EQ(kFreeChannel, client_control->channels[0].state); - EXPECT_EQ(kBusyChannel, client_control->channels[1].state); - EXPECT_EQ(kBusyChannel, client_control->channels[2].state); - EXPECT_EQ(kFreeChannel, client_control->channels[3].state); - EXPECT_EQ(kFreeChannel, client_control->channels[4].state); - EXPECT_EQ(kFreeChannel, client_control->channels[5].state); - - delete[] reinterpret_cast(client_control); -} - -TEST(IPCTest, CrossCallStrPacking) { - // This test tries the CrossCall object with null and non-null string - // combination of parameters, integer types and verifies that the unpacker - // can read them properly. - size_t base_start = 0; - IPCControl* client_control = - MakeChannels(kIPCChannelSize, 4096 * 4, &base_start); - client_control->server_alive = HANDLE(1); - FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); - - char* mem = reinterpret_cast(client_control); - SharedMemIPCClient client(mem); - - CrossCallReturn answer; - uint32 tag1 = 666; - const wchar_t text[] = L"98765 - 43210"; - std::wstring copied_text; - CrossCallParamsEx* actual_params; - - CrossCall(client, tag1, text, &answer); - actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(1, actual_params->GetParamsCount()); - EXPECT_EQ(tag1, actual_params->GetTag()); - EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); - EXPECT_STREQ(text, copied_text.c_str()); - - // Check with an empty string. - uint32 tag2 = 777; - const wchar_t* null_text = NULL; - CrossCall(client, tag2, null_text, &answer); - actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(1, actual_params->GetParamsCount()); - EXPECT_EQ(tag2, actual_params->GetTag()); - uint32 param_size = 1; - ArgType type = INVALID_TYPE; - void* param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); - EXPECT_TRUE(NULL != param_addr); - EXPECT_EQ(0, param_size); - EXPECT_EQ(WCHAR_TYPE, type); - EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); - - uint32 tag3 = 888; - param_size = 1; - copied_text.clear(); - - // Check with an empty string and a non-empty string. - CrossCall(client, tag3, null_text, text, &answer); - actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(2, actual_params->GetParamsCount()); - EXPECT_EQ(tag3, actual_params->GetTag()); - type = INVALID_TYPE; - param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); - EXPECT_TRUE(NULL != param_addr); - EXPECT_EQ(0, param_size); - EXPECT_EQ(WCHAR_TYPE, type); - EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); - EXPECT_TRUE(actual_params->GetParameterStr(1, &copied_text)); - EXPECT_STREQ(text, copied_text.c_str()); - - param_size = 1; - std::wstring copied_text_p0, copied_text_p2; - - const wchar_t text2[] = L"AeFG"; - CrossCall(client, tag1, text2, null_text, text, &answer); - actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(3, actual_params->GetParamsCount()); - EXPECT_EQ(tag1, actual_params->GetTag()); - EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text_p0)); - EXPECT_STREQ(text2, copied_text_p0.c_str()); - EXPECT_TRUE(actual_params->GetParameterStr(2, &copied_text_p2)); - EXPECT_STREQ(text, copied_text_p2.c_str()); - type = INVALID_TYPE; - param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); - EXPECT_TRUE(NULL != param_addr); - EXPECT_EQ(0, param_size); - EXPECT_EQ(WCHAR_TYPE, type); - - CloseChannelEvents(client_control); - delete[] reinterpret_cast(client_control); -} - -TEST(IPCTest, CrossCallIntPacking) { - // Check handling for regular 32 bit integers used in Windows. - size_t base_start = 0; - IPCControl* client_control = - MakeChannels(kIPCChannelSize, 4096 * 4, &base_start); - client_control->server_alive = HANDLE(1); - FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); - - uint32 tag1 = 999; - uint32 tag2 = 111; - const wchar_t text[] = L"godzilla"; - CrossCallParamsEx* actual_params; - - char* mem = reinterpret_cast(client_control); - SharedMemIPCClient client(mem); - - CrossCallReturn answer; - DWORD dw = 0xE6578; - CrossCall(client, tag2, dw, &answer); - actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(1, actual_params->GetParamsCount()); - EXPECT_EQ(tag2, actual_params->GetTag()); - ArgType type = INVALID_TYPE; - uint32 param_size = 1; - void* param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); - ASSERT_EQ(sizeof(dw), param_size); - EXPECT_EQ(ULONG_TYPE, type); - ASSERT_TRUE(NULL != param_addr); - EXPECT_EQ(0, memcmp(&dw, param_addr, param_size)); - - // Check handling for windows HANDLES. - HANDLE h = HANDLE(0x70000500); - CrossCall(client, tag1, text, h, &answer); - actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(2, actual_params->GetParamsCount()); - EXPECT_EQ(tag1, actual_params->GetTag()); - type = INVALID_TYPE; - param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); - ASSERT_EQ(sizeof(h), param_size); - EXPECT_EQ(VOIDPTR_TYPE, type); - ASSERT_TRUE(NULL != param_addr); - EXPECT_EQ(0, memcmp(&h, param_addr, param_size)); - - // Check combination of 32 and 64 bits. - CrossCall(client, tag2, h, dw, h, &answer); - actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(3, actual_params->GetParamsCount()); - EXPECT_EQ(tag2, actual_params->GetTag()); - type = INVALID_TYPE; - param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); - ASSERT_EQ(sizeof(h), param_size); - EXPECT_EQ(VOIDPTR_TYPE, type); - ASSERT_TRUE(NULL != param_addr); - EXPECT_EQ(0, memcmp(&h, param_addr, param_size)); - type = INVALID_TYPE; - param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); - ASSERT_EQ(sizeof(dw), param_size); - EXPECT_EQ(ULONG_TYPE, type); - ASSERT_TRUE(NULL != param_addr); - EXPECT_EQ(0, memcmp(&dw, param_addr, param_size)); - type = INVALID_TYPE; - param_addr = actual_params->GetRawParameter(2, ¶m_size, &type); - ASSERT_EQ(sizeof(h), param_size); - EXPECT_EQ(VOIDPTR_TYPE, type); - ASSERT_TRUE(NULL != param_addr); - EXPECT_EQ(0, memcmp(&h, param_addr, param_size)); - - CloseChannelEvents(client_control); - delete[] reinterpret_cast(client_control); -} - -TEST(IPCTest, CrossCallValidation) { - // First a sanity test with a well formed parameter object. - unsigned long value = 124816; - const uint32 kTag = 33; - const uint32 kBufferSize = 256; - ActualCallParams<1, kBufferSize> params_1(kTag); - params_1.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); - void* buffer = const_cast(params_1.GetBuffer()); - - uint32 out_size = 0; - CrossCallParamsEx* ccp = 0; - ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(), - &out_size); - ASSERT_TRUE(NULL != ccp); - EXPECT_TRUE(ccp->GetBuffer() != buffer); - EXPECT_EQ(kTag, ccp->GetTag()); - EXPECT_EQ(1, ccp->GetParamsCount()); - delete[] (reinterpret_cast(ccp)); - -#if defined(NDEBUG) - // Test hat we handle integer overflow on the number of params - // correctly. We use a test-only ctor for ActualCallParams that - // allows to create malformed cross-call buffers. - const int32 kPtrDiffSz = sizeof(ptrdiff_t); - for (int32 ix = -1; ix != 3; ++ix) { - uint32 fake_num_params = (kuint32max / kPtrDiffSz) + ix; - ActualCallParams<1, kBufferSize> params_2(kTag, fake_num_params); - params_2.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); - buffer = const_cast(params_2.GetBuffer()); - - EXPECT_TRUE(NULL != buffer); - ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(), - &out_size); - // If the buffer is malformed the return is NULL. - EXPECT_TRUE(NULL == ccp); - } -#endif // defined(NDEBUG) - - ActualCallParams<1, kBufferSize> params_3(kTag, 1); - params_3.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); - buffer = const_cast(params_3.GetBuffer()); - EXPECT_TRUE(NULL != buffer); - - uint32 correct_size = params_3.OverrideSize(1); - ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); - EXPECT_TRUE(NULL == ccp); - - // The correct_size is 8 bytes aligned. - params_3.OverrideSize(correct_size - 7); - ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); - EXPECT_TRUE(NULL == ccp); - - params_3.OverrideSize(correct_size); - ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); - EXPECT_TRUE(NULL != ccp); - - // Make sure that two parameters work as expected. - ActualCallParams<2, kBufferSize> params_4(kTag, 2); - params_4.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); - params_4.CopyParamIn(1, buffer, sizeof(buffer), false, VOIDPTR_TYPE); - buffer = const_cast(params_4.GetBuffer()); - EXPECT_TRUE(NULL != buffer); - - ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); - EXPECT_TRUE(NULL != ccp); - -#if defined(_WIN64) - correct_size = params_4.OverrideSize(1); - params_4.OverrideSize(correct_size - 1); - ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); - EXPECT_TRUE(NULL == ccp); -#endif -} - -// This structure is passed to the mock server threads to simulate -// the server side IPC so it has the required kernel objects. -struct ServerEvents { - HANDLE ping; - HANDLE pong; - volatile LONG* state; - HANDLE mutex; -}; - -// This is the server thread that quicky answers an IPC and exits. -DWORD WINAPI QuickResponseServer(PVOID param) { - ServerEvents* events = reinterpret_cast(param); - DWORD wait_result = 0; - wait_result = ::WaitForSingleObject(events->ping, INFINITE); - ::InterlockedExchange(events->state, kAckChannel); - ::SetEvent(events->pong); - return wait_result; -} - -class CrossCallParamsMock : public CrossCallParams { - public: - CrossCallParamsMock(uint32 tag, uint32 params_count) - : CrossCallParams(tag, params_count) { - } - private: - void* params[4]; -}; - -void FakeOkAnswerInChannel(void* channel) { - CrossCallReturn* answer = reinterpret_cast(channel); - answer->call_outcome = SBOX_ALL_OK; -} - -// Create two threads that will quickly answer IPCs; the first one -// using channel 1 (channel 0 is busy) and one using channel 0. No time-out -// should occur. -TEST(IPCTest, ClientFastServer) { - const size_t channel_size = kIPCChannelSize; - size_t base_start = 0; - IPCControl* client_control = - MakeChannels(channel_size, 4096 * 2, &base_start); - FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY); - client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL); - - char* mem = reinterpret_cast(client_control); - SharedMemIPCClient client(mem); - - ServerEvents events = {0}; - events.ping = client_control->channels[1].ping_event; - events.pong = client_control->channels[1].pong_event; - events.state = &client_control->channels[1].state; - - HANDLE t1 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL); - ASSERT_TRUE(NULL != t1); - ::CloseHandle(t1); - - void* buff0 = client.GetBuffer(); - EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0); - EXPECT_EQ(kBusyChannel, client_control->channels[0].state); - EXPECT_EQ(kFreeChannel, client_control->channels[1].state); - EXPECT_EQ(kFreeChannel, client_control->channels[2].state); - - void* buff1 = client.GetBuffer(); - EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1); - EXPECT_EQ(kBusyChannel, client_control->channels[0].state); - EXPECT_EQ(kBusyChannel, client_control->channels[1].state); - EXPECT_EQ(kFreeChannel, client_control->channels[2].state); - - EXPECT_EQ(0, client_control->channels[1].ipc_tag); - - uint32 tag = 7654; - CrossCallReturn answer; - CrossCallParamsMock* params1 = new(buff1) CrossCallParamsMock(tag, 1); - FakeOkAnswerInChannel(buff1); - - ResultCode result = client.DoCall(params1, &answer); - if (SBOX_ERROR_CHANNEL_ERROR != result) - client.FreeBuffer(buff1); - - EXPECT_TRUE(SBOX_ALL_OK == result); - EXPECT_EQ(tag, client_control->channels[1].ipc_tag); - EXPECT_EQ(kBusyChannel, client_control->channels[0].state); - EXPECT_EQ(kFreeChannel, client_control->channels[1].state); - EXPECT_EQ(kFreeChannel, client_control->channels[2].state); - - HANDLE t2 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL); - ASSERT_TRUE(NULL != t2); - ::CloseHandle(t2); - - client.FreeBuffer(buff0); - events.ping = client_control->channels[0].ping_event; - events.pong = client_control->channels[0].pong_event; - events.state = &client_control->channels[0].state; - - tag = 4567; - CrossCallParamsMock* params2 = new(buff0) CrossCallParamsMock(tag, 1); - FakeOkAnswerInChannel(buff0); - - result = client.DoCall(params2, &answer); - if (SBOX_ERROR_CHANNEL_ERROR != result) - client.FreeBuffer(buff0); - - EXPECT_TRUE(SBOX_ALL_OK == result); - EXPECT_EQ(tag, client_control->channels[0].ipc_tag); - EXPECT_EQ(kFreeChannel, client_control->channels[0].state); - EXPECT_EQ(kFreeChannel, client_control->channels[1].state); - EXPECT_EQ(kFreeChannel, client_control->channels[2].state); - - CloseChannelEvents(client_control); - ::CloseHandle(client_control->server_alive); - - delete[] reinterpret_cast(client_control); -} - -// This is the server thread that very slowly answers an IPC and exits. Note -// that the pong event needs to be signaled twice. -DWORD WINAPI SlowResponseServer(PVOID param) { - ServerEvents* events = reinterpret_cast(param); - DWORD wait_result = 0; - wait_result = ::WaitForSingleObject(events->ping, INFINITE); - ::Sleep(kIPCWaitTimeOut1 + kIPCWaitTimeOut2 + 200); - ::InterlockedExchange(events->state, kAckChannel); - ::SetEvent(events->pong); - return wait_result; -} - -// This thread's job is to keep the mutex locked. -DWORD WINAPI MainServerThread(PVOID param) { - ServerEvents* events = reinterpret_cast(param); - DWORD wait_result = 0; - wait_result = ::WaitForSingleObject(events->mutex, INFINITE); - Sleep(kIPCWaitTimeOut1 * 20); - return wait_result; -} - -// Creates a server thread that answers the IPC so slow that is guaranteed to -// trigger the time-out code path in the client. A second thread is created -// to hold locked the server_alive mutex: this signals the client that the -// server is not dead and it retries the wait. -TEST(IPCTest, ClientSlowServer) { - size_t base_start = 0; - IPCControl* client_control = - MakeChannels(kIPCChannelSize, 4096*2, &base_start); - FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY); - client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL); - - char* mem = reinterpret_cast(client_control); - SharedMemIPCClient client(mem); - - ServerEvents events = {0}; - events.ping = client_control->channels[0].ping_event; - events.pong = client_control->channels[0].pong_event; - events.state = &client_control->channels[0].state; - - HANDLE t1 = ::CreateThread(NULL, 0, SlowResponseServer, &events, 0, NULL); - ASSERT_TRUE(NULL != t1); - ::CloseHandle(t1); - - ServerEvents events2 = {0}; - events2.pong = events.pong; - events2.mutex = client_control->server_alive; - - HANDLE t2 = ::CreateThread(NULL, 0, MainServerThread, &events2, 0, NULL); - ASSERT_TRUE(NULL != t2); - ::CloseHandle(t2); - - ::Sleep(1); - - void* buff0 = client.GetBuffer(); - uint32 tag = 4321; - CrossCallReturn answer; - CrossCallParamsMock* params1 = new(buff0) CrossCallParamsMock(tag, 1); - FakeOkAnswerInChannel(buff0); - - ResultCode result = client.DoCall(params1, &answer); - if (SBOX_ERROR_CHANNEL_ERROR != result) - client.FreeBuffer(buff0); - - EXPECT_TRUE(SBOX_ALL_OK == result); - EXPECT_EQ(tag, client_control->channels[0].ipc_tag); - EXPECT_EQ(kFreeChannel, client_control->channels[0].state); - - CloseChannelEvents(client_control); - ::CloseHandle(client_control->server_alive); - delete[] reinterpret_cast(client_control); -} - -// This test-only IPC dispatcher has two handlers with the same signature -// but only CallOneHandler should be used. -class UnitTestIPCDispatcher : public Dispatcher { - public: - enum { - CALL_ONE_TAG = 78, - CALL_TWO_TAG = 87 - }; - - UnitTestIPCDispatcher(); - ~UnitTestIPCDispatcher() {}; - - virtual bool SetupService(InterceptionManager* manager, int service) { - return true; - } - - private: - bool CallOneHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) { - ipc->return_info.extended[0].handle = p1; - ipc->return_info.extended[1].unsigned_int = p2; - return true; - } - - bool CallTwoHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) { - return true; - } -}; - -UnitTestIPCDispatcher::UnitTestIPCDispatcher() { - static const IPCCall call_one = { - {CALL_ONE_TAG, VOIDPTR_TYPE, ULONG_TYPE}, - reinterpret_cast( - &UnitTestIPCDispatcher::CallOneHandler) - }; - static const IPCCall call_two = { - {CALL_TWO_TAG, VOIDPTR_TYPE, ULONG_TYPE}, - reinterpret_cast( - &UnitTestIPCDispatcher::CallTwoHandler) - }; - ipc_calls_.push_back(call_one); - ipc_calls_.push_back(call_two); -} - -// This test does most of the shared memory IPC client-server roundtrip -// and tests the packing, unpacking and call dispatching. -TEST(IPCTest, SharedMemServerTests) { - size_t base_start = 0; - IPCControl* client_control = - MakeChannels(kIPCChannelSize, 4096, &base_start); - client_control->server_alive = HANDLE(1); - FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); - - char* mem = reinterpret_cast(client_control); - SharedMemIPCClient client(mem); - - CrossCallReturn answer; - HANDLE bar = HANDLE(191919); - DWORD foo = 6767676; - CrossCall(client, UnitTestIPCDispatcher::CALL_ONE_TAG, bar, foo, &answer); - void* buff = client.GetBuffer(); - ASSERT_TRUE(NULL != buff); - - UnitTestIPCDispatcher dispatcher; - // Since we are directly calling InvokeCallback, most of this structure - // can be set to NULL. - sandbox::SharedMemIPCServer::ServerControl srv_control = { - NULL, NULL, kIPCChannelSize, NULL, - reinterpret_cast(client_control), - NULL, &dispatcher, {0} }; - - sandbox::CrossCallReturn call_return = {0}; - EXPECT_TRUE(SharedMemIPCServer::InvokeCallback(&srv_control, buff, - &call_return)); - EXPECT_EQ(SBOX_ALL_OK, call_return.call_outcome); - EXPECT_TRUE(bar == call_return.extended[0].handle); - EXPECT_EQ(foo, call_return.extended[1].unsigned_int); - - CloseChannelEvents(client_control); - delete[] reinterpret_cast(client_control); -} - -} // namespace sandbox diff --git a/sandbox/src/job.cc b/sandbox/src/job.cc deleted file mode 100644 index 8ed3a77..0000000 --- a/sandbox/src/job.cc +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/job.h" - -#include "base/win/windows_version.h" -#include "sandbox/src/restricted_token.h" - -namespace sandbox { - -Job::~Job() { - if (job_handle_) - ::CloseHandle(job_handle_); -}; - -DWORD Job::Init(JobLevel security_level, wchar_t *job_name, - DWORD ui_exceptions) { - if (job_handle_) - return ERROR_ALREADY_INITIALIZED; - - job_handle_ = ::CreateJobObject(NULL, // No security attribute - job_name); - if (!job_handle_) - return ::GetLastError(); - - JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; - JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; - - // Set the settings for the different security levels. Note: The higher levels - // inherit from the lower levels. - switch (security_level) { - case JOB_LOCKDOWN: { - jeli.BasicLimitInformation.LimitFlags |= - JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; - } - case JOB_RESTRICTED: { - jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD; - jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_READCLIPBOARD; - jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; - jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_GLOBALATOMS; - } - case JOB_LIMITED_USER: { - jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DISPLAYSETTINGS; - jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS; - jeli.BasicLimitInformation.ActiveProcessLimit = 1; - } - case JOB_INTERACTIVE: { - jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS; - jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DESKTOP; - jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS; - } - case JOB_UNPROTECTED: { - // The JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag is not supported on - // Windows 2000. We need a mechanism on Windows 2000 to ensure - // that processes in the job are terminated when the job is closed - if (base::win::GetVersion() == base::win::VERSION_PRE_XP) - break; - - jeli.BasicLimitInformation.LimitFlags |= - JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - break; - } - default: { - return ERROR_BAD_ARGUMENTS; - } - } - - if (FALSE == ::SetInformationJobObject(job_handle_, - JobObjectExtendedLimitInformation, - &jeli, - sizeof(jeli))) { - return ::GetLastError(); - } - - jbur.UIRestrictionsClass = jbur.UIRestrictionsClass & (~ui_exceptions); - if (FALSE == ::SetInformationJobObject(job_handle_, - JobObjectBasicUIRestrictions, - &jbur, - sizeof(jbur))) { - return ::GetLastError(); - } - - return ERROR_SUCCESS; -} - -DWORD Job::UserHandleGrantAccess(HANDLE handle) { - if (!job_handle_) - return ERROR_NO_DATA; - - if (!::UserHandleGrantAccess(handle, - job_handle_, - TRUE)) { // Access allowed. - return ::GetLastError(); - } - - return ERROR_SUCCESS; -} - -HANDLE Job::Detach() { - HANDLE handle_temp = job_handle_; - job_handle_ = NULL; - return handle_temp; -} - -DWORD Job::AssignProcessToJob(HANDLE process_handle) { - if (!job_handle_) - return ERROR_NO_DATA; - - if (FALSE == ::AssignProcessToJobObject(job_handle_, process_handle)) - return ::GetLastError(); - - return ERROR_SUCCESS; -} - -} // namespace sandbox diff --git a/sandbox/src/job.h b/sandbox/src/job.h deleted file mode 100644 index a3f6738..0000000 --- a/sandbox/src/job.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_JOB_H_ -#define SANDBOX_SRC_JOB_H_ - -#include "base/basictypes.h" -#include "sandbox/src/restricted_token_utils.h" - -namespace sandbox { - -// Handles the creation of job objects based on a security profile. -// Sample usage: -// Job job; -// job.Init(JOB_LOCKDOWN, NULL); //no job name -// job.AssignProcessToJob(process_handle); -class Job { - public: - Job() : job_handle_(NULL) { } - - ~Job(); - - // Initializes and creates the job object. The security of the job is based - // on the security_level parameter. - // job_name can be NULL if the job is unnamed. - // If the chosen profile has too many ui restrictions, you can disable some - // by specifying them in the ui_exceptions parameters. - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - DWORD Init(JobLevel security_level, wchar_t *job_name, DWORD ui_exceptions); - - // Assigns the process referenced by process_handle to the job. - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - DWORD AssignProcessToJob(HANDLE process_handle); - - // Grants access to "handle" to the job. All processes in the job can - // subsequently recognize and use the handle. - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - DWORD UserHandleGrantAccess(HANDLE handle); - - // Revokes ownership to the job handle and returns it. The destructor of the - // class won't close the handle when called. - // If the object is not yet initialized, it returns 0. - HANDLE Detach(); - - private: - // Handle to the job referenced by the object. - HANDLE job_handle_; - - DISALLOW_COPY_AND_ASSIGN(Job); -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_JOB_H_ diff --git a/sandbox/src/job_unittest.cc b/sandbox/src/job_unittest.cc deleted file mode 100644 index f386f1f..0000000 --- a/sandbox/src/job_unittest.cc +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains unit tests for the job object. - -#include "base/win/scoped_process_information.h" -#include "sandbox/src/job.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -// Tests the creation and destruction of the job. -TEST(JobTest, TestCreation) { - // Scope the creation of Job. - { - // Create the job. - Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); - - // check if the job exists. - HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, - L"my_test_job_name"); - ASSERT_TRUE(job_handle != NULL); - - if (job_handle) - CloseHandle(job_handle); - } - - // Check if the job is destroyed when the object goes out of scope. - HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, L"my_test_job_name"); - ASSERT_TRUE(job_handle == NULL); - ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); -} - -// Tests the method "Detach". -TEST(JobTest, TestDetach) { - HANDLE job_handle; - // Scope the creation of Job. - { - // Create the job. - Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); - - job_handle = job.Detach(); - ASSERT_TRUE(job_handle != NULL); - } - - // Check to be sure that the job is still alive even after the object is gone - // out of scope. - HANDLE job_handle_dup = ::OpenJobObjectW(GENERIC_ALL, FALSE, - L"my_test_job_name"); - ASSERT_TRUE(job_handle_dup != NULL); - - // Remove all references. - if (job_handle_dup) - ::CloseHandle(job_handle_dup); - - if (job_handle) - ::CloseHandle(job_handle); - - // Check if the jbo is really dead. - job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, L"my_test_job_name"); - ASSERT_TRUE(job_handle == NULL); - ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); -} - -// Tests the ui exceptions -TEST(JobTest, TestExceptions) { - HANDLE job_handle; - // Scope the creation of Job. - { - // Create the job. - Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", - JOB_OBJECT_UILIMIT_READCLIPBOARD)); - - job_handle = job.Detach(); - ASSERT_TRUE(job_handle != NULL); - - JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; - DWORD size = sizeof(jbur); - BOOL result = ::QueryInformationJobObject(job_handle, - JobObjectBasicUIRestrictions, - &jbur, size, &size); - ASSERT_TRUE(result); - - ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD, 0); - ::CloseHandle(job_handle); - } - - // Scope the creation of Job. - { - // Create the job. - Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); - - job_handle = job.Detach(); - ASSERT_TRUE(job_handle != NULL); - - JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; - DWORD size = sizeof(jbur); - BOOL result = ::QueryInformationJobObject(job_handle, - JobObjectBasicUIRestrictions, - &jbur, size, &size); - ASSERT_TRUE(result); - - ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD, - JOB_OBJECT_UILIMIT_READCLIPBOARD); - ::CloseHandle(job_handle); - } -} - -// Tests the error case when the job is initialized twice. -TEST(JobTest, DoubleInit) { - // Create the job. - Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); - ASSERT_EQ(ERROR_ALREADY_INITIALIZED, job.Init(JOB_LOCKDOWN, L"test", 0)); -} - -// Tests the error case when we use a method and the object is not yet -// initialized. -TEST(JobTest, NoInit) { - Job job; - ASSERT_EQ(ERROR_NO_DATA, job.UserHandleGrantAccess(NULL)); - ASSERT_EQ(ERROR_NO_DATA, job.AssignProcessToJob(NULL)); - ASSERT_TRUE(job.Detach() == NULL); -} - -// Tests the initialization of the job with different security level. -TEST(JobTest, SecurityLevel) { - Job job1; - ASSERT_EQ(ERROR_SUCCESS, job1.Init(JOB_LOCKDOWN, L"job1", 0)); - - Job job2; - ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0)); - - Job job3; - ASSERT_EQ(ERROR_SUCCESS, job3.Init(JOB_LIMITED_USER, L"job3", 0)); - - Job job4; - ASSERT_EQ(ERROR_SUCCESS, job4.Init(JOB_INTERACTIVE, L"job4", 0)); - - Job job5; - ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 0)); - - Job job6; - ASSERT_EQ(ERROR_BAD_ARGUMENTS, job6.Init( - static_cast(JOB_UNPROTECTED+1), L"job6", 0)); -} - -// Tests the method "AssignProcessToJob". -TEST(JobTest, ProcessInJob) { - // Create the job. - Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_UNPROTECTED, L"job_test_process", 0)); - - BOOL result = FALSE; - - wchar_t notepad[] = L"notepad"; - STARTUPINFO si = { sizeof(si) }; - base::win::ScopedProcessInformation pi; - result = ::CreateProcess(NULL, notepad, NULL, NULL, FALSE, 0, NULL, NULL, &si, - pi.Receive()); - ASSERT_TRUE(result); - ASSERT_EQ(ERROR_SUCCESS, job.AssignProcessToJob(pi.process_handle())); - - // Get the job handle. - HANDLE job_handle = job.Detach(); - - // Check if the process is in the job. - JOBOBJECT_BASIC_PROCESS_ID_LIST jbpidl = {0}; - DWORD size = sizeof(jbpidl); - result = ::QueryInformationJobObject(job_handle, - JobObjectBasicProcessIdList, - &jbpidl, size, &size); - EXPECT_TRUE(result); - - EXPECT_EQ(1, jbpidl.NumberOfAssignedProcesses); - EXPECT_EQ(1, jbpidl.NumberOfProcessIdsInList); - EXPECT_EQ(pi.process_id(), jbpidl.ProcessIdList[0]); - - EXPECT_TRUE(::TerminateProcess(pi.process_handle(), 0)); - - EXPECT_TRUE(::CloseHandle(job_handle)); -} - -} // namespace sandbox diff --git a/sandbox/src/named_pipe_dispatcher.cc b/sandbox/src/named_pipe_dispatcher.cc deleted file mode 100644 index 0569784..0000000 --- a/sandbox/src/named_pipe_dispatcher.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/named_pipe_dispatcher.h" - -#include "base/basictypes.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/interception.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/named_pipe_interception.h" -#include "sandbox/src/named_pipe_policy.h" -#include "sandbox/src/policy_broker.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox.h" - - -namespace sandbox { - -NamedPipeDispatcher::NamedPipeDispatcher(PolicyBase* policy_base) - : policy_base_(policy_base) { - static const IPCCall create_params = { - {IPC_CREATENAMEDPIPEW_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE, - ULONG_TYPE, ULONG_TYPE, ULONG_TYPE}, - reinterpret_cast(&NamedPipeDispatcher::CreateNamedPipe) - }; - - ipc_calls_.push_back(create_params); -} - -bool NamedPipeDispatcher::SetupService(InterceptionManager* manager, - int service) { - if (IPC_CREATENAMEDPIPEW_TAG == service) - return INTERCEPT_EAT(manager, L"kernel32.dll", CreateNamedPipeW, - CREATE_NAMED_PIPE_ID, 36); - - return false; -} - -bool NamedPipeDispatcher::CreateNamedPipe( - IPCInfo* ipc, std::wstring* name, DWORD open_mode, DWORD pipe_mode, - DWORD max_instances, DWORD out_buffer_size, DWORD in_buffer_size, - DWORD default_timeout) { - const wchar_t* pipe_name = name->c_str(); - CountedParameterSet params; - params[NameBased::NAME] = ParamPickerMake(pipe_name); - - EvalResult eval = policy_base_->EvalPolicy(IPC_CREATENAMEDPIPEW_TAG, - params.GetBase()); - - HANDLE pipe; - DWORD ret = NamedPipePolicy::CreateNamedPipeAction(eval, *ipc->client_info, - *name, open_mode, - pipe_mode, max_instances, - out_buffer_size, - in_buffer_size, - default_timeout, &pipe); - - ipc->return_info.win32_result = ret; - ipc->return_info.handle = pipe; - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/named_pipe_dispatcher.h b/sandbox/src/named_pipe_dispatcher.h deleted file mode 100644 index 87a68b6..0000000 --- a/sandbox/src/named_pipe_dispatcher.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__ -#define SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__ - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sandbox_policy_base.h" - -namespace sandbox { - -// This class handles named pipe related IPC calls. -class NamedPipeDispatcher : public Dispatcher { - public: - explicit NamedPipeDispatcher(PolicyBase* policy_base); - ~NamedPipeDispatcher() {} - - // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); - - private: - // Processes IPC requests coming from calls to CreateNamedPipeW() in the - // target. - bool CreateNamedPipe(IPCInfo* ipc, std::wstring* name, DWORD open_mode, - DWORD pipe_mode, DWORD max_instances, - DWORD out_buffer_size, DWORD in_buffer_size, - DWORD default_timeout); - - PolicyBase* policy_base_; - DISALLOW_COPY_AND_ASSIGN(NamedPipeDispatcher); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__ diff --git a/sandbox/src/named_pipe_interception.cc b/sandbox/src/named_pipe_interception.cc deleted file mode 100644 index 4599441..0000000 --- a/sandbox/src/named_pipe_interception.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/named_pipe_interception.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/policy_target.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/target_services.h" - -namespace sandbox { - -HANDLE WINAPI TargetCreateNamedPipeW( - CreateNamedPipeWFunction orig_CreateNamedPipeW, LPCWSTR pipe_name, - DWORD open_mode, DWORD pipe_mode, DWORD max_instance, DWORD out_buffer_size, - DWORD in_buffer_size, DWORD default_timeout, - LPSECURITY_ATTRIBUTES security_attributes) { - HANDLE pipe = orig_CreateNamedPipeW(pipe_name, open_mode, pipe_mode, - max_instance, out_buffer_size, - in_buffer_size, default_timeout, - security_attributes); - if (INVALID_HANDLE_VALUE != pipe) - return pipe; - - DWORD original_error = ::GetLastError(); - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return INVALID_HANDLE_VALUE; - - // We don't support specific Security Attributes. - if (security_attributes) - return INVALID_HANDLE_VALUE; - - do { - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - CountedParameterSet params; - params[NameBased::NAME] = ParamPickerMake(pipe_name); - - if (!QueryBroker(IPC_CREATENAMEDPIPEW_TAG, params.GetBase())) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_CREATENAMEDPIPEW_TAG, pipe_name, - open_mode, pipe_mode, max_instance, - out_buffer_size, in_buffer_size, - default_timeout, &answer); - if (SBOX_ALL_OK != code) - break; - - ::SetLastError(answer.win32_result); - - if (ERROR_SUCCESS != answer.win32_result) - return INVALID_HANDLE_VALUE; - - return answer.handle; - } while (false); - - ::SetLastError(original_error); - return INVALID_HANDLE_VALUE; -} - -} // namespace sandbox diff --git a/sandbox/src/named_pipe_interception.h b/sandbox/src/named_pipe_interception.h deleted file mode 100644 index 5e2b334..0000000 --- a/sandbox/src/named_pipe_interception.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -#ifndef SANDBOX_SRC_NAMED_PIPE_INTERCEPTION_H__ -#define SANDBOX_SRC_NAMED_PIPE_INTERCEPTION_H__ - -namespace sandbox { - -extern "C" { - -typedef HANDLE (WINAPI *CreateNamedPipeWFunction) ( - LPCWSTR lpName, - DWORD dwOpenMode, - DWORD dwPipeMode, - DWORD nMaxInstances, - DWORD nOutBufferSize, - DWORD nInBufferSize, - DWORD nDefaultTimeOut, - LPSECURITY_ATTRIBUTES lpSecurityAttributes); - -// Interception of CreateNamedPipeW in kernel32.dll -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateNamedPipeW( - CreateNamedPipeWFunction orig_CreateNamedPipeW, LPCWSTR pipe_name, - DWORD open_mode, DWORD pipe_mode, DWORD max_instance, DWORD out_buffer_size, - DWORD in_buffer_size, DWORD default_timeout, - LPSECURITY_ATTRIBUTES security_attributes); - -} // extern "C" - -} // namespace sandbox - -#endif // SANDBOX_SRC_NAMED_PIPE_INTERCEPTION_H__ diff --git a/sandbox/src/named_pipe_policy.cc b/sandbox/src/named_pipe_policy.cc deleted file mode 100644 index 00182cf..0000000 --- a/sandbox/src/named_pipe_policy.cc +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/named_pipe_policy.h" - -#include - -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_engine_opcodes.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox_types.h" - -namespace { - -// Creates a named pipe and duplicates the handle to 'target_process'. The -// remaining parameters are the same as CreateNamedPipeW(). -HANDLE CreateNamedPipeHelper(HANDLE target_process, LPCWSTR pipe_name, - DWORD open_mode, DWORD pipe_mode, - DWORD max_instances, DWORD out_buffer_size, - DWORD in_buffer_size, DWORD default_timeout, - LPSECURITY_ATTRIBUTES security_attributes) { - HANDLE pipe = ::CreateNamedPipeW(pipe_name, open_mode, pipe_mode, - max_instances, out_buffer_size, - in_buffer_size, default_timeout, - security_attributes); - if (INVALID_HANDLE_VALUE == pipe) - return pipe; - - HANDLE new_pipe; - if (!::DuplicateHandle(::GetCurrentProcess(), pipe, target_process, &new_pipe, - 0, FALSE, DUPLICATE_CLOSE_SOURCE | - DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(pipe); - return INVALID_HANDLE_VALUE; - } - - return new_pipe; -} - -} // namespace - -namespace sandbox { - -bool NamedPipePolicy::GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy) { - if (TargetPolicy::NAMEDPIPES_ALLOW_ANY != semantics) { - return false; - } - PolicyRule pipe(ASK_BROKER); - if (!pipe.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) { - return false; - } - if (!policy->AddRule(IPC_CREATENAMEDPIPEW_TAG, &pipe)) { - return false; - } - return true; -} - -DWORD NamedPipePolicy::CreateNamedPipeAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &name, - DWORD open_mode, DWORD pipe_mode, - DWORD max_instances, - DWORD out_buffer_size, - DWORD in_buffer_size, - DWORD default_timeout, - HANDLE* pipe) { - // The only action supported is ASK_BROKER which means create the pipe. - if (ASK_BROKER != eval_result) { - return ERROR_ACCESS_DENIED; - } - - *pipe = CreateNamedPipeHelper(client_info.process, name.c_str(), - open_mode, pipe_mode, max_instances, - out_buffer_size, in_buffer_size, - default_timeout, NULL); - - if (INVALID_HANDLE_VALUE == *pipe) - return ERROR_ACCESS_DENIED; - - return ERROR_SUCCESS; -} - -} // namespace sandbox diff --git a/sandbox/src/named_pipe_policy.h b/sandbox/src/named_pipe_policy.h deleted file mode 100644 index 2b6b09d..0000000 --- a/sandbox/src/named_pipe_policy.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_NAMED_PIPE_POLICY_H__ -#define SANDBOX_SRC_NAMED_PIPE_POLICY_H__ - -#include - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/policy_low_level.h" -#include "sandbox/src/sandbox_policy.h" - -namespace sandbox { - -enum EvalResult; - -// This class centralizes most of the knowledge related to named pipe creation. -class NamedPipePolicy { - public: - // Creates the required low-level policy rules to evaluate a high-level. - // policy rule for named pipe creation - // 'name' is the named pipe to be created - // 'semantics' is the desired semantics. - // 'policy' is the policy generator to which the rules are going to be added. - static bool GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy); - - // Processes a 'CreateNamedPipeW()' request from the target. - static DWORD CreateNamedPipeAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &name, - DWORD open_mode, DWORD pipe_mode, - DWORD max_instances, - DWORD out_buffer_size, - DWORD in_buffer_size, - DWORD default_timeout, HANDLE* pipe); -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_NAMED_PIPE_POLICY_H__ diff --git a/sandbox/src/named_pipe_policy_test.cc b/sandbox/src/named_pipe_policy_test.cc deleted file mode 100644 index 11ddbc3..0000000 --- a/sandbox/src/named_pipe_policy_test.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/tests/common/controller.h" - -namespace sandbox { - - -SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t **argv) { - if (argc != 1) { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - if ((NULL == argv) || (NULL == argv[0])) { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - - HANDLE pipe = ::CreateNamedPipeW(argv[0], - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 4096, - 4096, 2000, NULL); - if (INVALID_HANDLE_VALUE == pipe) - return SBOX_TEST_DENIED; - - OVERLAPPED overlapped = {0}; - overlapped.hEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL); - BOOL result = ::ConnectNamedPipe(pipe, &overlapped); - - if (!result) { - DWORD error = ::GetLastError(); - if (ERROR_PIPE_CONNECTED != error && - ERROR_IO_PENDING != error) { - return SBOX_TEST_FAILED; - } - } - - if (!::CloseHandle(pipe)) - return SBOX_TEST_FAILED; - - ::CloseHandle(overlapped.hEvent); - return SBOX_TEST_SUCCEEDED; -} - -// Tests if we can create a pipe in the sandbox. On XP, the sandbox can create -// a pipe without any help but it fails on Vista, this is why we do not test -// the "denied" case. -TEST(NamedPipePolicyTest, CreatePipe) { - TestRunner runner; - // TODO(nsylvain): This policy is wrong because "*" is a valid char in a - // namedpipe name. Here we apply it like a wildcard. http://b/893603 - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES, - TargetPolicy::NAMEDPIPES_ALLOW_ANY, - L"\\\\.\\pipe\\test*")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); -} - -// The same test as CreatePipe but this time using strict interceptions. -TEST(NamedPipePolicyTest, CreatePipeStrictInterceptions) { - TestRunner runner; - runner.GetPolicy()->SetStrictInterceptions(); - - // TODO(nsylvain): This policy is wrong because "*" is a valid char in a - // namedpipe name. Here we apply it like a wildcard. http://b/893603 - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES, - TargetPolicy::NAMEDPIPES_ALLOW_ANY, - L"\\\\.\\pipe\\test*")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); -} - -} // namespace sandbox diff --git a/sandbox/src/nt_internals.h b/sandbox/src/nt_internals.h deleted file mode 100644 index fe4fcd6..0000000 --- a/sandbox/src/nt_internals.h +++ /dev/null @@ -1,611 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file holds definitions related to the ntdll API. - -#ifndef SANDBOX_SRC_NT_INTERNALS_H__ -#define SANDBOX_SRC_NT_INTERNALS_H__ - -#include - -typedef LONG NTSTATUS; -#define NT_SUCCESS(st) (st >= 0) - -#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) -#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) -#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) -#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L) -#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) -#ifndef STATUS_INVALID_PARAMETER -// It is now defined in Windows 2008 SDK. -#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL) -#endif -#define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018L) -#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) -#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) -#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L) -#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL) -#define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL) -#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL) - -#define CURRENT_PROCESS ((HANDLE) -1) -#define CURRENT_THREAD ((HANDLE) -2) -#define NtCurrentProcess CURRENT_PROCESS - -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING; -typedef UNICODE_STRING *PUNICODE_STRING; -typedef const UNICODE_STRING *PCUNICODE_STRING; - -typedef struct _STRING { - USHORT Length; - USHORT MaximumLength; - PCHAR Buffer; -} STRING; -typedef STRING *PSTRING; - -typedef STRING ANSI_STRING; -typedef PSTRING PANSI_STRING; -typedef CONST PSTRING PCANSI_STRING; - -typedef STRING OEM_STRING; -typedef PSTRING POEM_STRING; -typedef CONST STRING* PCOEM_STRING; - -#define OBJ_CASE_INSENSITIVE 0x00000040L - -typedef struct _OBJECT_ATTRIBUTES { - ULONG Length; - HANDLE RootDirectory; - PUNICODE_STRING ObjectName; - ULONG Attributes; - PVOID SecurityDescriptor; - PVOID SecurityQualityOfService; -} OBJECT_ATTRIBUTES; -typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES; - -#define InitializeObjectAttributes(p, n, a, r, s) { \ - (p)->Length = sizeof(OBJECT_ATTRIBUTES);\ - (p)->RootDirectory = r;\ - (p)->Attributes = a;\ - (p)->ObjectName = n;\ - (p)->SecurityDescriptor = s;\ - (p)->SecurityQualityOfService = NULL;\ -} - -typedef struct _IO_STATUS_BLOCK { - union { - NTSTATUS Status; - PVOID Pointer; - }; - ULONG_PTR Information; -} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; - -// ----------------------------------------------------------------------- -// File IO - -// Create disposition values. - -#define FILE_SUPERSEDE 0x00000000 -#define FILE_OPEN 0x00000001 -#define FILE_CREATE 0x00000002 -#define FILE_OPEN_IF 0x00000003 -#define FILE_OVERWRITE 0x00000004 -#define FILE_OVERWRITE_IF 0x00000005 -#define FILE_MAXIMUM_DISPOSITION 0x00000005 - -// Create/open option flags. - -#define FILE_DIRECTORY_FILE 0x00000001 -#define FILE_WRITE_THROUGH 0x00000002 -#define FILE_SEQUENTIAL_ONLY 0x00000004 -#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 - -#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 -#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 -#define FILE_NON_DIRECTORY_FILE 0x00000040 -#define FILE_CREATE_TREE_CONNECTION 0x00000080 - -#define FILE_COMPLETE_IF_OPLOCKED 0x00000100 -#define FILE_NO_EA_KNOWLEDGE 0x00000200 -#define FILE_OPEN_REMOTE_INSTANCE 0x00000400 -#define FILE_RANDOM_ACCESS 0x00000800 - -#define FILE_DELETE_ON_CLOSE 0x00001000 -#define FILE_OPEN_BY_FILE_ID 0x00002000 -#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 -#define FILE_NO_COMPRESSION 0x00008000 - -#define FILE_RESERVE_OPFILTER 0x00100000 -#define FILE_OPEN_REPARSE_POINT 0x00200000 -#define FILE_OPEN_NO_RECALL 0x00400000 -#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000 - -typedef NTSTATUS (WINAPI *NtCreateFileFunction)( - OUT PHANDLE FileHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PLARGE_INTEGER AllocationSize OPTIONAL, - IN ULONG FileAttributes, - IN ULONG ShareAccess, - IN ULONG CreateDisposition, - IN ULONG CreateOptions, - IN PVOID EaBuffer OPTIONAL, - IN ULONG EaLength); - -typedef NTSTATUS (WINAPI *NtOpenFileFunction)( - OUT PHANDLE FileHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN ULONG ShareAccess, - IN ULONG OpenOptions); - -typedef NTSTATUS (WINAPI *NtCloseFunction)( - IN HANDLE Handle); - -typedef enum _FILE_INFORMATION_CLASS { - FileRenameInformation = 10 -} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; - -typedef struct _FILE_RENAME_INFORMATION { - BOOLEAN ReplaceIfExists; - HANDLE RootDirectory; - ULONG FileNameLength; - WCHAR FileName[1]; -} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; - -typedef NTSTATUS (WINAPI *NtSetInformationFileFunction)( - IN HANDLE FileHandle, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PVOID FileInformation, - IN ULONG Length, - IN FILE_INFORMATION_CLASS FileInformationClass); - -typedef struct FILE_BASIC_INFORMATION { - LARGE_INTEGER CreationTime; - LARGE_INTEGER LastAccessTime; - LARGE_INTEGER LastWriteTime; - LARGE_INTEGER ChangeTime; - ULONG FileAttributes; -} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; - -typedef NTSTATUS (WINAPI *NtQueryAttributesFileFunction)( - IN POBJECT_ATTRIBUTES ObjectAttributes, - OUT PFILE_BASIC_INFORMATION FileAttributes); - -typedef struct _FILE_NETWORK_OPEN_INFORMATION { - LARGE_INTEGER CreationTime; - LARGE_INTEGER LastAccessTime; - LARGE_INTEGER LastWriteTime; - LARGE_INTEGER ChangeTime; - LARGE_INTEGER AllocationSize; - LARGE_INTEGER EndOfFile; - ULONG FileAttributes; -} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION; - -typedef NTSTATUS (WINAPI *NtQueryFullAttributesFileFunction)( - IN POBJECT_ATTRIBUTES ObjectAttributes, - OUT PFILE_NETWORK_OPEN_INFORMATION FileAttributes); - -// ----------------------------------------------------------------------- -// Sections - -typedef NTSTATUS (WINAPI *NtCreateSectionFunction)( - OUT PHANDLE SectionHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN PLARGE_INTEGER MaximumSize OPTIONAL, - IN ULONG SectionPageProtection, - IN ULONG AllocationAttributes, - IN HANDLE FileHandle OPTIONAL); - -typedef ULONG SECTION_INHERIT; -#define ViewShare 1 -#define ViewUnmap 2 - -typedef NTSTATUS (WINAPI *NtMapViewOfSectionFunction)( - IN HANDLE SectionHandle, - IN HANDLE ProcessHandle, - IN OUT PVOID *BaseAddress, - IN ULONG_PTR ZeroBits, - IN SIZE_T CommitSize, - IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, - IN OUT PSIZE_T ViewSize, - IN SECTION_INHERIT InheritDisposition, - IN ULONG AllocationType, - IN ULONG Win32Protect); - -typedef NTSTATUS (WINAPI *NtUnmapViewOfSectionFunction)( - IN HANDLE ProcessHandle, - IN PVOID BaseAddress); - -typedef enum _SECTION_INFORMATION_CLASS { - SectionBasicInformation = 0, - SectionImageInformation -} SECTION_INFORMATION_CLASS; - -typedef struct _SECTION_BASIC_INFORMATION { - PVOID BaseAddress; - ULONG Attributes; - LARGE_INTEGER Size; -} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; - -typedef NTSTATUS (WINAPI *NtQuerySectionFunction)( - IN HANDLE SectionHandle, - IN SECTION_INFORMATION_CLASS SectionInformationClass, - OUT PVOID SectionInformation, - IN SIZE_T SectionInformationLength, - OUT PSIZE_T ReturnLength OPTIONAL); - -// ----------------------------------------------------------------------- -// Process and Thread - -typedef struct _CLIENT_ID { - PVOID UniqueProcess; - PVOID UniqueThread; -} CLIENT_ID, *PCLIENT_ID; - -typedef NTSTATUS (WINAPI *NtOpenThreadFunction) ( - OUT PHANDLE ThreadHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN PCLIENT_ID ClientId); - -typedef NTSTATUS (WINAPI *NtOpenProcessFunction) ( - OUT PHANDLE ProcessHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN PCLIENT_ID ClientId); - -typedef enum _NT_THREAD_INFORMATION_CLASS { - ThreadBasicInformation, - ThreadTimes, - ThreadPriority, - ThreadBasePriority, - ThreadAffinityMask, - ThreadImpersonationToken, - ThreadDescriptorTableEntry, - ThreadEnableAlignmentFaultFixup, - ThreadEventPair, - ThreadQuerySetWin32StartAddress, - ThreadZeroTlsCell, - ThreadPerformanceCount, - ThreadAmILastThread, - ThreadIdealProcessor, - ThreadPriorityBoost, - ThreadSetTlsArrayAddress, - ThreadIsIoPending, - ThreadHideFromDebugger -} NT_THREAD_INFORMATION_CLASS, *PNT_THREAD_INFORMATION_CLASS; - -typedef NTSTATUS (WINAPI *NtSetInformationThreadFunction) ( - IN HANDLE ThreadHandle, - IN NT_THREAD_INFORMATION_CLASS ThreadInformationClass, - IN PVOID ThreadInformation, - IN ULONG ThreadInformationLength); - -// Partial definition only: -typedef enum _PROCESSINFOCLASS { - ProcessBasicInformation = 0 -} PROCESSINFOCLASS; - -typedef PVOID PPEB; -typedef PVOID KPRIORITY; - -typedef struct _PROCESS_BASIC_INFORMATION { - NTSTATUS ExitStatus; - PPEB PebBaseAddress; - KAFFINITY AffinityMask; - KPRIORITY BasePriority; - ULONG UniqueProcessId; - ULONG InheritedFromUniqueProcessId; -} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; - -typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)( - IN HANDLE ProcessHandle, - IN PROCESSINFOCLASS ProcessInformationClass, - OUT PVOID ProcessInformation, - IN ULONG ProcessInformationLength, - OUT PULONG ReturnLength OPTIONAL); - -typedef NTSTATUS (WINAPI *NtOpenThreadTokenFunction) ( - IN HANDLE ThreadHandle, - IN ACCESS_MASK DesiredAccess, - IN BOOLEAN OpenAsSelf, - OUT PHANDLE TokenHandle); - -typedef NTSTATUS (WINAPI *NtOpenThreadTokenExFunction) ( - IN HANDLE ThreadHandle, - IN ACCESS_MASK DesiredAccess, - IN BOOLEAN OpenAsSelf, - IN ULONG HandleAttributes, - OUT PHANDLE TokenHandle); - -typedef NTSTATUS (WINAPI *NtOpenProcessTokenFunction) ( - IN HANDLE ProcessHandle, - IN ACCESS_MASK DesiredAccess, - OUT PHANDLE TokenHandle); - -typedef NTSTATUS (WINAPI *NtOpenProcessTokenExFunction) ( - IN HANDLE ProcessHandle, - IN ACCESS_MASK DesiredAccess, - IN ULONG HandleAttributes, - OUT PHANDLE TokenHandle); - -typedef NTSTATUS (WINAPI * RtlCreateUserThreadFunction)( - IN HANDLE Process, - IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, - IN BOOLEAN CreateSuspended, - IN ULONG ZeroBits, - IN SIZE_T MaximumStackSize, - IN SIZE_T CommittedStackSize, - IN LPTHREAD_START_ROUTINE StartAddress, - IN PVOID Parameter, - OUT PHANDLE Thread, - OUT PCLIENT_ID ClientId); - -// ----------------------------------------------------------------------- -// Registry - -typedef NTSTATUS (WINAPI *NtCreateKeyFunction)( - OUT PHANDLE KeyHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN ULONG TitleIndex, - IN PUNICODE_STRING Class OPTIONAL, - IN ULONG CreateOptions, - OUT PULONG Disposition OPTIONAL); - -typedef NTSTATUS (WINAPI *NtOpenKeyFunction)( - OUT PHANDLE KeyHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI *NtOpenKeyExFunction)( - OUT PHANDLE KeyHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN DWORD open_options); - -typedef NTSTATUS (WINAPI *NtDeleteKeyFunction)( - IN HANDLE KeyHandle); - -// ----------------------------------------------------------------------- -// Memory - -// Don't really need this structure right now. -typedef PVOID PRTL_HEAP_PARAMETERS; - -typedef PVOID (WINAPI *RtlCreateHeapFunction)( - IN ULONG Flags, - IN PVOID HeapBase OPTIONAL, - IN SIZE_T ReserveSize OPTIONAL, - IN SIZE_T CommitSize OPTIONAL, - IN PVOID Lock OPTIONAL, - IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL); - -typedef PVOID (WINAPI *RtlDestroyHeapFunction)( - IN PVOID HeapHandle); - -typedef PVOID (WINAPI *RtlAllocateHeapFunction)( - IN PVOID HeapHandle, - IN ULONG Flags, - IN SIZE_T Size); - -typedef BOOLEAN (WINAPI *RtlFreeHeapFunction)( - IN PVOID HeapHandle, - IN ULONG Flags, - IN PVOID HeapBase); - -typedef NTSTATUS (WINAPI *NtAllocateVirtualMemoryFunction) ( - IN HANDLE ProcessHandle, - IN OUT PVOID *BaseAddress, - IN ULONG_PTR ZeroBits, - IN OUT PSIZE_T RegionSize, - IN ULONG AllocationType, - IN ULONG Protect); - -typedef NTSTATUS (WINAPI *NtFreeVirtualMemoryFunction) ( - IN HANDLE ProcessHandle, - IN OUT PVOID *BaseAddress, - IN OUT PSIZE_T RegionSize, - IN ULONG FreeType); - -typedef enum _MEMORY_INFORMATION_CLASS { - MemoryBasicInformation = 0, - MemoryWorkingSetList, - MemorySectionName, - MemoryBasicVlmInformation -} MEMORY_INFORMATION_CLASS; - -typedef struct _MEMORY_SECTION_NAME { // Information Class 2 - UNICODE_STRING SectionFileName; -} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME; - -typedef NTSTATUS (WINAPI *NtQueryVirtualMemoryFunction)( - IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN MEMORY_INFORMATION_CLASS MemoryInformationClass, - OUT PVOID MemoryInformation, - IN ULONG MemoryInformationLength, - OUT PULONG ReturnLength OPTIONAL); - -typedef NTSTATUS (WINAPI *NtProtectVirtualMemoryFunction)( - IN HANDLE ProcessHandle, - IN OUT PVOID* BaseAddress, - IN OUT PSIZE_T ProtectSize, - IN ULONG NewProtect, - OUT PULONG OldProtect); - -// ----------------------------------------------------------------------- -// Objects - -typedef enum _OBJECT_INFORMATION_CLASS { - ObjectBasicInformation, - ObjectNameInformation, - ObjectTypeInformation, - ObjectAllInformation, - ObjectDataInformation -} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS; - -typedef struct _OBJDIR_INFORMATION { - UNICODE_STRING ObjectName; - UNICODE_STRING ObjectTypeName; - BYTE Data[1]; -} OBJDIR_INFORMATION; - -typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION { - ULONG Attributes; - ACCESS_MASK GrantedAccess; - ULONG HandleCount; - ULONG PointerCount; - ULONG Reserved[10]; // reserved for internal use -} PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION; - -typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION { - UNICODE_STRING TypeName; - ULONG Reserved[22]; // reserved for internal use -} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION; - -typedef enum _POOL_TYPE { - NonPagedPool, - PagedPool, - NonPagedPoolMustSucceed, - ReservedType, - NonPagedPoolCacheAligned, - PagedPoolCacheAligned, - NonPagedPoolCacheAlignedMustS -} POOL_TYPE; - -typedef struct _OBJECT_BASIC_INFORMATION { - ULONG Attributes; - ACCESS_MASK GrantedAccess; - ULONG HandleCount; - ULONG PointerCount; - ULONG PagedPoolUsage; - ULONG NonPagedPoolUsage; - ULONG Reserved[3]; - ULONG NameInformationLength; - ULONG TypeInformationLength; - ULONG SecurityDescriptorLength; - LARGE_INTEGER CreateTime; -} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION; - -typedef struct _OBJECT_TYPE_INFORMATION { - UNICODE_STRING Name; - ULONG TotalNumberOfObjects; - ULONG TotalNumberOfHandles; - ULONG TotalPagedPoolUsage; - ULONG TotalNonPagedPoolUsage; - ULONG TotalNamePoolUsage; - ULONG TotalHandleTableUsage; - ULONG HighWaterNumberOfObjects; - ULONG HighWaterNumberOfHandles; - ULONG HighWaterPagedPoolUsage; - ULONG HighWaterNonPagedPoolUsage; - ULONG HighWaterNamePoolUsage; - ULONG HighWaterHandleTableUsage; - ULONG InvalidAttributes; - GENERIC_MAPPING GenericMapping; - ULONG ValidAccess; - BOOLEAN SecurityRequired; - BOOLEAN MaintainHandleCount; - USHORT MaintainTypeList; - POOL_TYPE PoolType; - ULONG PagedPoolUsage; - ULONG NonPagedPoolUsage; -} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; - -typedef enum _SYSTEM_INFORMATION_CLASS { - SystemHandleInformation = 16 -} SYSTEM_INFORMATION_CLASS; - -typedef struct _SYSTEM_HANDLE_INFORMATION { - USHORT ProcessId; - USHORT CreatorBackTraceIndex; - UCHAR ObjectTypeNumber; - UCHAR Flags; - USHORT Handle; - PVOID Object; - ACCESS_MASK GrantedAccess; -} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; - -typedef struct _SYSTEM_HANDLE_INFORMATION_EX { - ULONG NumberOfHandles; - SYSTEM_HANDLE_INFORMATION Information[1]; -} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; - -typedef struct _OBJECT_NAME_INFORMATION { - UNICODE_STRING ObjectName; -} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; - -typedef NTSTATUS (WINAPI *NtQueryObjectFunction)( - IN HANDLE Handle, - IN OBJECT_INFORMATION_CLASS ObjectInformationClass, - OUT PVOID ObjectInformation OPTIONAL, - IN ULONG ObjectInformationLength, - OUT PULONG ReturnLength OPTIONAL); - -typedef NTSTATUS (WINAPI *NtDuplicateObjectFunction)( - IN HANDLE SourceProcess, - IN HANDLE SourceHandle, - IN HANDLE TargetProcess, - OUT PHANDLE TargetHandle, - IN ACCESS_MASK DesiredAccess, - IN ULONG Attributes, - IN ULONG Options); - -typedef NTSTATUS (WINAPI *NtSignalAndWaitForSingleObjectFunction)( - IN HANDLE HandleToSignal, - IN HANDLE HandleToWait, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER Timeout OPTIONAL); - -typedef NTSTATUS (WINAPI *NtQuerySystemInformation)( - IN SYSTEM_INFORMATION_CLASS SystemInformationClass, - OUT PVOID SystemInformation, - IN ULONG SystemInformationLength, - OUT PULONG ReturnLength); - -typedef NTSTATUS (WINAPI *NtQueryObject)( - IN HANDLE Handle, - IN OBJECT_INFORMATION_CLASS ObjectInformationClass, - OUT PVOID ObjectInformation, - IN ULONG ObjectInformationLength, - OUT PULONG ReturnLength); - -// ----------------------------------------------------------------------- -// Strings - -typedef int (__cdecl *_strnicmpFunction)( - IN const char* _Str1, - IN const char* _Str2, - IN size_t _MaxCount); - -typedef size_t (__cdecl *strlenFunction)( - IN const char * _Str); - -typedef size_t (__cdecl *wcslenFunction)( - IN const wchar_t* _Str); - -typedef NTSTATUS (WINAPI *RtlAnsiStringToUnicodeStringFunction)( - IN OUT PUNICODE_STRING DestinationString, - IN PANSI_STRING SourceString, - IN BOOLEAN AllocateDestinationString); - -typedef LONG (WINAPI *RtlCompareUnicodeStringFunction)( - IN PCUNICODE_STRING String1, - IN PCUNICODE_STRING String2, - IN BOOLEAN CaseInSensitive); - -typedef VOID (WINAPI *RtlInitUnicodeStringFunction) ( - IN OUT PUNICODE_STRING DestinationString, - IN PCWSTR SourceString); - -#endif // SANDBOX_SRC_NT_INTERNALS_H__ diff --git a/sandbox/src/policy_broker.cc b/sandbox/src/policy_broker.cc deleted file mode 100644 index 61c64c6..0000000 --- a/sandbox/src/policy_broker.cc +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "sandbox/src/policy_broker.h" - -#include "base/logging.h" -#include "base/win/pe_image.h" -#include "base/win/windows_version.h" -#include "sandbox/src/interception.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/policy_target.h" -#include "sandbox/src/process_thread_interception.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_nt_types.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/sandbox_utils.h" -#include "sandbox/src/target_process.h" - -// This code executes on the broker side, as a callback from the policy on the -// target side (the child). - -namespace sandbox { - -// This is the list of all imported symbols from ntdll.dll. -SANDBOX_INTERCEPT NtExports g_nt; - -#define INIT_GLOBAL_NT(member) \ - g_nt.member = reinterpret_cast( \ - ntdll_image.GetProcAddress("Nt" #member)); \ - if (NULL == g_nt.member) \ - return false - -#define INIT_GLOBAL_RTL(member) \ - g_nt.member = reinterpret_cast( \ - ntdll_image.GetProcAddress(#member)); \ - if (NULL == g_nt.member) \ - return false - -bool SetupNtdllImports(TargetProcess *child) { - HMODULE ntdll = ::GetModuleHandle(kNtdllName); - base::win::PEImage ntdll_image(ntdll); - - // Bypass purify's interception. - wchar_t* loader_get = reinterpret_cast( - ntdll_image.GetProcAddress("LdrGetDllHandle")); - if (loader_get) { - GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - loader_get, &ntdll); - } - - INIT_GLOBAL_NT(AllocateVirtualMemory); - INIT_GLOBAL_NT(Close); - INIT_GLOBAL_NT(DuplicateObject); - INIT_GLOBAL_NT(FreeVirtualMemory); - INIT_GLOBAL_NT(MapViewOfSection); - INIT_GLOBAL_NT(ProtectVirtualMemory); - INIT_GLOBAL_NT(QueryInformationProcess); - INIT_GLOBAL_NT(QueryObject); - INIT_GLOBAL_NT(QuerySection); - INIT_GLOBAL_NT(QueryVirtualMemory); - INIT_GLOBAL_NT(UnmapViewOfSection); - - INIT_GLOBAL_RTL(RtlAllocateHeap); - INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString); - INIT_GLOBAL_RTL(RtlCompareUnicodeString); - INIT_GLOBAL_RTL(RtlCreateHeap); - INIT_GLOBAL_RTL(RtlCreateUserThread); - INIT_GLOBAL_RTL(RtlDestroyHeap); - INIT_GLOBAL_RTL(RtlFreeHeap); - INIT_GLOBAL_RTL(_strnicmp); - INIT_GLOBAL_RTL(strlen); - INIT_GLOBAL_RTL(wcslen); - -#ifndef NDEBUG - // Verify that the structure is fully initialized. - for (size_t i = 0; i < sizeof(g_nt)/sizeof(void*); i++) - DCHECK(reinterpret_cast(&g_nt)[i]); -#endif - ResultCode ret = child->TransferVariable("g_nt", &g_nt, sizeof(g_nt)); - - return SBOX_ALL_OK == ret ? true : false; -} - -#undef INIT_GLOBAL_NT -#undef INIT_GLOBAL_RTL - -bool SetupBasicInterceptions(InterceptionManager* manager) { - // Interceptions provided by process_thread_policy, without actual policy. - if (!INTERCEPT_NT(manager, NtOpenThread, OPEN_TREAD_ID, 20) || - !INTERCEPT_NT(manager, NtOpenProcess, OPEN_PROCESS_ID, 20) || - !INTERCEPT_NT(manager, NtOpenProcessToken, OPEN_PROCESS_TOKEN_ID, 16)) - return false; - - // Interceptions with neither policy nor IPC. - if (!INTERCEPT_NT(manager, NtSetInformationThread, SET_INFORMATION_THREAD_ID, - 20) || - !INTERCEPT_NT(manager, NtOpenThreadToken, OPEN_THREAD_TOKEN_ID, 20)) - return false; - - if (base::win::GetVersion() >= base::win::VERSION_XP) { - // Bug 27218: We don't have dispatch for some x64 syscalls. - // This one is also provided by process_thread_policy. - if (!INTERCEPT_NT(manager, NtOpenProcessTokenEx, OPEN_PROCESS_TOKEN_EX_ID, - 20)) - return false; - - return INTERCEPT_NT(manager, NtOpenThreadTokenEx, OPEN_THREAD_TOKEN_EX_ID, - 24); - } - - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/policy_broker.h b/sandbox/src/policy_broker.h deleted file mode 100644 index fd2602a..0000000 --- a/sandbox/src/policy_broker.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_POLICY_BROKER_H_ -#define SANDBOX_SRC_POLICY_BROKER_H_ - -#include "sandbox/src/interception.h" - -namespace sandbox { - -class TargetProcess; - -// Sets up interceptions not controlled by explicit policies. -bool SetupBasicInterceptions(InterceptionManager* manager); - -// Sets up imports from NTDLL for the given target process so the interceptions -// can work. -bool SetupNtdllImports(TargetProcess *child); - -} // namespace sandbox - -#endif // SANDBOX_SRC_POLICY_BROKER_H_ diff --git a/sandbox/src/policy_engine_opcodes.cc b/sandbox/src/policy_engine_opcodes.cc deleted file mode 100644 index 8d9ceef..0000000 --- a/sandbox/src/policy_engine_opcodes.cc +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/policy_engine_opcodes.h" - -#include "base/basictypes.h" -#include "sandbox/src/sandbox_nt_types.h" -#include "sandbox/src/sandbox_types.h" - -namespace { -const unsigned short kMaxUniStrSize = 0xfffc; - -bool InitStringUnicode(const wchar_t* source, size_t length, - UNICODE_STRING* ustring) { - ustring->Buffer = const_cast(source); - ustring->Length = static_cast(length) * sizeof(wchar_t); - if (length > kMaxUniStrSize) { - return false; - } - ustring->MaximumLength = (NULL != source) ? - ustring->Length + sizeof(wchar_t) : 0; - return true; -} - -} // namespace - -namespace sandbox { - -SANDBOX_INTERCEPT NtExports g_nt; - -// Note: The opcodes are implemented as functions (as opposed to classes derived -// from PolicyOpcode) because you should not add more member variables to the -// PolicyOpcode class since it would cause object slicing on the target. So to -// enforce that (instead of just trusting the developer) the opcodes became -// just functions. -// -// In the code that follows I have keep the evaluation function and the factory -// function together to stress the close relationship between both. For example, -// only the factory method and the evaluation function know the stored argument -// order and meaning. - -template -EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp, - MatchContext* match); - -////////////////////////////////////////////////////////////////////////////// -// Opcode OpAlwaysFalse: -// Does not require input parameter. - -PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) { - return MakeBase(OP_ALWAYS_FALSE, options, -1); -} - -template <> -EvalResult OpcodeEval(PolicyOpcode* opcode, - const ParameterSet* param, - MatchContext* context) { - UNREFERENCED_PARAMETER(opcode); - UNREFERENCED_PARAMETER(param); - UNREFERENCED_PARAMETER(context); - return EVAL_FALSE; -} - -////////////////////////////////////////////////////////////////////////////// -// Opcode OpAlwaysTrue: -// Does not require input parameter. - -PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) { - return MakeBase(OP_ALWAYS_TRUE, options, -1); -} - -template <> -EvalResult OpcodeEval(PolicyOpcode* opcode, - const ParameterSet* param, - MatchContext* context) { - UNREFERENCED_PARAMETER(opcode); - UNREFERENCED_PARAMETER(param); - UNREFERENCED_PARAMETER(context); - return EVAL_TRUE; -} - -////////////////////////////////////////////////////////////////////////////// -// Opcode OpAction: -// Does not require input parameter. -// Argument 0 contains the actual action to return. - -PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action, - uint32 options) { - PolicyOpcode* opcode = MakeBase(OP_ACTION, options, 0); - if (NULL == opcode) return NULL; - opcode->SetArgument(0, action); - return opcode; -} - -template <> -EvalResult OpcodeEval(PolicyOpcode* opcode, - const ParameterSet* param, - MatchContext* context) { - UNREFERENCED_PARAMETER(param); - UNREFERENCED_PARAMETER(context); - int action = 0; - opcode->GetArgument(0, &action); - return static_cast(action); -} - -////////////////////////////////////////////////////////////////////////////// -// Opcode OpNumberMatch: -// Requires a unsigned long or void* in selected_param -// Argument 0 is the stored number to match. -// Argument 1 is the C++ type of the 0th argument. - -PolicyOpcode* OpcodeFactory::MakeOpNumberMatch(int16 selected_param, - unsigned long match, - uint32 options) { - PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); - if (NULL == opcode) return NULL; - opcode->SetArgument(0, match); - opcode->SetArgument(1, ULONG_TYPE); - return opcode; -} - -PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16 selected_param, - const void* match, - uint32 options) { - PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); - if (NULL == opcode) return NULL; - opcode->SetArgument(0, match); - opcode->SetArgument(1, VOIDPTR_TYPE); - return opcode; -} - -template <> -EvalResult OpcodeEval(PolicyOpcode* opcode, - const ParameterSet* param, - MatchContext* context) { - UNREFERENCED_PARAMETER(context); - unsigned long value_ulong = 0; - if (param->Get(&value_ulong)) { - unsigned long match_ulong = 0; - opcode->GetArgument(0, &match_ulong); - return (match_ulong != value_ulong)? EVAL_FALSE : EVAL_TRUE; - } else { - const void* value_ptr = NULL; - if (param->Get(&value_ptr)) { - const void* match_ptr = NULL; - opcode->GetArgument(0, &match_ptr); - return (match_ptr != value_ptr)? EVAL_FALSE : EVAL_TRUE; - } - } - return EVAL_ERROR; -} - -////////////////////////////////////////////////////////////////////////////// -// Opcode OpUlongMatchRange -// Requires a unsigned long in selected_param. -// Argument 0 is the stored lower bound to match. -// Argument 1 is the stored upper bound to match. - -PolicyOpcode* OpcodeFactory::MakeOpUlongMatchRange(int16 selected_param, - unsigned long lower_bound, - unsigned long upper_bound, - uint32 options) { - if (lower_bound > upper_bound) { - return false; - } - PolicyOpcode* opcode = MakeBase(OP_ULONG_MATCH_RANGE, options, - selected_param); - if (NULL == opcode) return NULL; - opcode->SetArgument(0, lower_bound); - opcode->SetArgument(1, upper_bound); - return opcode; -} - -template <> -EvalResult OpcodeEval(PolicyOpcode* opcode, - const ParameterSet* param, - MatchContext* context) { - UNREFERENCED_PARAMETER(context); - unsigned long value = 0; - if (!param->Get(&value)) return EVAL_ERROR; - - unsigned long lower_bound = 0; - unsigned long upper_bound = 0; - opcode->GetArgument(0, &lower_bound); - opcode->GetArgument(1, &upper_bound); - return((lower_bound <= value) && (upper_bound >= value))? - EVAL_TRUE : EVAL_FALSE; -} - -////////////////////////////////////////////////////////////////////////////// -// Opcode OpUlongAndMatch: -// Requires a unsigned long in selected_param. -// Argument 0 is the stored number to match. - -PolicyOpcode* OpcodeFactory::MakeOpUlongAndMatch(int16 selected_param, - unsigned long match, - uint32 options) { - PolicyOpcode* opcode = MakeBase(OP_ULONG_AND_MATCH, options, selected_param); - if (NULL == opcode) return NULL; - opcode->SetArgument(0, match); - return opcode; -} - -template <> -EvalResult OpcodeEval(PolicyOpcode* opcode, - const ParameterSet* param, - MatchContext* context) { - UNREFERENCED_PARAMETER(context); - unsigned long value = 0; - if (!param->Get(&value)) return EVAL_ERROR; - - unsigned long number = 0; - opcode->GetArgument(0, &number); - return (number & value)? EVAL_TRUE : EVAL_FALSE; -} - -////////////////////////////////////////////////////////////////////////////// -// Opcode OpWStringMatch: -// Requires a wchar_t* in selected_param. -// Argument 0 is the byte displacement of the stored string. -// Argument 1 is the lenght in chars of the stored string. -// Argument 2 is the offset to apply on the input string. It has special values. -// as noted in the header file. -// Argument 3 is the string matching options. - -PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16 selected_param, - const wchar_t* match_str, - int start_position, - StringMatchOptions match_opts, - uint32 options) { - if (NULL == match_str) { - return NULL; - } - if ('\0' == match_str[0]) { - return NULL; - } - - int lenght = lstrlenW(match_str); - - PolicyOpcode* opcode = MakeBase(OP_WSTRING_MATCH, options, selected_param); - if (NULL == opcode) { - return NULL; - } - ptrdiff_t delta_str = AllocRelative(opcode, match_str, wcslen(match_str)+1); - if (0 == delta_str) { - return NULL; - } - opcode->SetArgument(0, delta_str); - opcode->SetArgument(1, lenght); - opcode->SetArgument(2, start_position); - opcode->SetArgument(3, match_opts); - return opcode; -} - -template<> -EvalResult OpcodeEval(PolicyOpcode* opcode, - const ParameterSet* param, - MatchContext* context) { - if (NULL == context) { - return EVAL_ERROR; - } - const wchar_t* source_str = NULL; - if (!param->Get(&source_str)) return EVAL_ERROR; - - int start_position = 0; - int match_len = 0; - unsigned int match_opts = 0; - opcode->GetArgument(1, &match_len); - opcode->GetArgument(2, &start_position); - opcode->GetArgument(3, &match_opts); - - const wchar_t* match_str = opcode->GetRelativeString(0); - // Advance the source string to the last successfully evaluated position - // according to the match context. - source_str = &source_str[context->position]; - int source_len = static_cast(g_nt.wcslen(source_str)); - - if (0 == source_len) { - // If we reached the end of the source string there is nothing we can - // match against. - return EVAL_FALSE; - } - if (match_len > source_len) { - // There can't be a positive match when the target string is bigger than - // the source string - return EVAL_FALSE; - } - - BOOL case_sensitive = (match_opts & CASE_INSENSITIVE) ? TRUE : FALSE; - - // We have three cases, depending on the value of start_pos: - // Case 1. We skip N characters and compare once. - // Case 2: We skip to the end and compare once. - // Case 3: We match the first substring (if we find any). - if (start_position >= 0) { - if (kSeekToEnd == start_position) { - start_position = source_len - match_len; - } else if (match_opts & EXACT_LENGHT) { - // A sub-case of case 3 is when the EXACT_LENGHT flag is on - // the match needs to be not just substring but full match. - if ((match_len + start_position) != source_len) { - return EVAL_FALSE; - } - } - - // Advance start_pos characters. Warning! this does not consider - // utf16 encodings (surrogate pairs) or other Unicode 'features'. - source_str += start_position; - - // Since we skipped, lets reevaluate just the lengths again. - if ((match_len + start_position) > source_len) { - return EVAL_FALSE; - } - - UNICODE_STRING match_ustr; - InitStringUnicode(match_str, match_len, &match_ustr); - UNICODE_STRING source_ustr; - InitStringUnicode(source_str, match_len, &source_ustr); - - if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, - case_sensitive)) { - // Match! update the match context. - context->position += start_position + match_len; - return EVAL_TRUE; - } else { - return EVAL_FALSE; - } - } else if (start_position < 0) { - UNICODE_STRING match_ustr; - InitStringUnicode(match_str, match_len, &match_ustr); - UNICODE_STRING source_ustr; - InitStringUnicode(source_str, match_len, &source_ustr); - - do { - if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, - case_sensitive)) { - // Match! update the match context. - context->position += (source_ustr.Buffer - source_str) + match_len; - return EVAL_TRUE; - } - ++source_ustr.Buffer; - --source_len; - } while (source_len >= match_len); - } - return EVAL_FALSE; -} - -////////////////////////////////////////////////////////////////////////////// -// OpcodeMaker (other member functions). - -PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id, - uint32 options, - int16 selected_param) { - if (memory_size() < sizeof(PolicyOpcode)) { - return NULL; - } - - // Create opcode using placement-new on the buffer memory. - PolicyOpcode* opcode = new(memory_top_) PolicyOpcode(); - - // Fill in the standard fields, that every opcode has. - memory_top_ += sizeof(PolicyOpcode); - opcode->opcode_id_ = opcode_id; - opcode->options_ = static_cast(options); - opcode->parameter_ = selected_param; - return opcode; -} - -ptrdiff_t OpcodeFactory::AllocRelative(void* start, const wchar_t* str, - size_t lenght) { - size_t bytes = lenght * sizeof(wchar_t); - if (memory_size() < bytes) { - return 0; - } - memory_bottom_ -= bytes; - if (reinterpret_cast(memory_bottom_) & 1) { - // TODO(cpu) replace this for something better. - ::DebugBreak(); - } - memcpy(memory_bottom_, str, bytes); - ptrdiff_t delta = memory_bottom_ - reinterpret_cast(start); - return delta; -} - -////////////////////////////////////////////////////////////////////////////// -// Opcode evaluation dispatchers. - -// This function is the one and only entry for evaluating any opcode. It is -// in charge of applying any relevant opcode options and calling EvaluateInner -// were the actual dispatch-by-id is made. It would seem at first glance that -// the dispatch should be done by virtual function (vtable) calls but you have -// to remember that the opcodes are made in the broker process and copied as -// raw memory to the target process. - -EvalResult PolicyOpcode::Evaluate(const ParameterSet* call_params, - size_t param_count, MatchContext* match) { - if (NULL == call_params) { - return EVAL_ERROR; - } - const ParameterSet* selected_param = NULL; - if (parameter_ >= 0) { - if (static_cast(parameter_) >= param_count) { - return EVAL_ERROR; - } - selected_param = &call_params[parameter_]; - } - EvalResult result = EvaluateHelper(selected_param, match); - - // Apply the general options regardless of the particular type of opcode. - if (kPolNone == options_) { - return result; - } - - if (options_ & kPolNegateEval) { - if (EVAL_TRUE == result) { - result = EVAL_FALSE; - } else if (EVAL_FALSE == result) { - result = EVAL_TRUE; - } else if (EVAL_ERROR != result) { - result = EVAL_ERROR; - } - } - if (NULL != match) { - if (options_ & kPolClearContext) { - match->Clear(); - } - if (options_ & kPolUseOREval) { - match->options = kPolUseOREval; - } - } - return result; -} - -#define OPCODE_EVAL(op, x, y, z) case op: return OpcodeEval(x, y, z) - -EvalResult PolicyOpcode::EvaluateHelper(const ParameterSet* parameters, - MatchContext* match) { - switch (opcode_id_) { - OPCODE_EVAL(OP_ALWAYS_FALSE, this, parameters, match); - OPCODE_EVAL(OP_ALWAYS_TRUE, this, parameters, match); - OPCODE_EVAL(OP_NUMBER_MATCH, this, parameters, match); - OPCODE_EVAL(OP_ULONG_MATCH_RANGE, this, parameters, match); - OPCODE_EVAL(OP_WSTRING_MATCH, this, parameters, match); - OPCODE_EVAL(OP_ULONG_AND_MATCH, this, parameters, match); - OPCODE_EVAL(OP_ACTION, this, parameters, match); - default: - return EVAL_ERROR; - } -} - -#undef OPCODE_EVAL - -} // namespace sandbox diff --git a/sandbox/src/policy_engine_opcodes.h b/sandbox/src/policy_engine_opcodes.h deleted file mode 100644 index 6821413..0000000 --- a/sandbox/src/policy_engine_opcodes.h +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_POLICY_ENGINE_OPCODES_H__ -#define SANDBOX_SRC_POLICY_ENGINE_OPCODES_H__ - -#include "sandbox/src/policy_engine_params.h" -#include "base/basictypes.h" - -// The low-level policy is implemented using the concept of policy 'opcodes'. -// An opcode is a structure that contains enough information to perform one -// comparison against one single input parameter. For example, an opcode can -// encode just one of the following comparison: -// -// - Is input parameter 3 not equal to NULL? -// - Does input parameter 2 start with L"c:\\"? -// - Is input parameter 5, bit 3 is equal 1? -// -// Each opcode is in fact equivalent to a function invocation where all -// the parameters are known by the opcode except one. So say you have a -// function of this form: -// bool fn(a, b, c, d) with 4 arguments -// -// Then an opcode is: -// op(fn, b, c, d) -// Which stores the function to call and its 3 last arguments -// -// Then and opcode evaluation is: -// op.eval(a) ------------------------> fn(a,b,c,d) -// internally calls -// -// The idea is that complex policy rules can be split into streams of -// opcodes which are evaluated in sequence. The evaluation is done in -// groups of opcodes that have N comparison opcodes plus 1 action opcode: -// -// [comparison 1][comparison 2]...[comparison N][action][comparison 1]... -// ----- evaluation order-----------> -// -// Each opcode group encodes one high-level policy rule. The rule applies -// only if all the conditions on the group evaluate to true. The action -// opcode contains the policy outcome for that particular rule. -// -// Note that this header contains the main building blocks of low-level policy -// but not the low level policy class. -namespace sandbox { - -// These are the possible policy outcomes. Note that some of them might -// not apply and can be removed. Also note that The following values only -// specify what to do, not how to do it and it is acceptable given specific -// cases to ignore the policy outcome. -enum EvalResult { - // Comparison opcode values: - EVAL_TRUE, // Opcode condition evaluated true. - EVAL_FALSE, // Opcode condition evaluated false. - EVAL_ERROR, // Opcode condition generated an error while evaluating. - // Action opcode values: - ASK_BROKER, // The target must generate an IPC to the broker. On the broker - // side, this means grant access to the resource. - DENY_ACCESS, // No access granted to the resource. - GIVE_READONLY, // Give readonly access to the resource. - GIVE_ALLACCESS, // Give full access to the resource. - GIVE_CACHED, // IPC is not required. Target can return a cached handle. - GIVE_FIRST, // TODO(cpu) - SIGNAL_ALARM, // Unusual activity. Generate an alarm. - FAKE_SUCCESS, // Do not call original function. Just return 'success'. - FAKE_ACCESS_DENIED, // Do not call original function. Just return 'denied' - // and do not do IPC. - TERMINATE_PROCESS, // Destroy target process. Do IPC as well. -}; - -// The following are the implemented opcodes. -enum OpcodeID { - OP_ALWAYS_FALSE, // Evaluates to false (EVAL_FALSE). - OP_ALWAYS_TRUE, // Evaluates to true (EVAL_TRUE). - OP_NUMBER_MATCH, // Match a 32-bit integer as n == a. - OP_ULONG_MATCH_RANGE, // Match an ulong integer as a <= n <= b. - OP_ULONG_AND_MATCH, // Match using bitwise AND; as in: n & a != 0. - OP_WSTRING_MATCH, // Match a string for equality. - OP_ACTION // Evaluates to an action opcode. -}; - -// Options that apply to every opcode. They are specified when creating -// each opcode using OpcodeFactory::MakeOpXXXXX() family of functions -// Do nothing special. -const uint32 kPolNone = 0; - -// Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express -// negated conditions such as if ( a && !b). -const uint32 kPolNegateEval = 1; - -// Zero the MatchContext context structure. This happens after the opcode -// is evaluated. -const uint32 kPolClearContext = 2; - -// Use OR when evaluating this set of opcodes. The policy evaluator by default -// uses AND when evaluating. Very helpful when -// used with kPolNegateEval. For example if you have a condition best expressed -// as if(! (a && b && c)), the use of this flags allows it to be expressed as -// if ((!a) || (!b) || (!c)). -const uint32 kPolUseOREval = 4; - -// Keeps the evaluation state between opcode evaluations. This is used -// for string matching where the next opcode needs to continue matching -// from the last character position from the current opcode. The match -// context is preserved across opcode evaluation unless an opcode specifies -// as an option kPolClearContext. -struct MatchContext { - size_t position; - uint32 options; - - MatchContext() { - Clear(); - } - - void Clear() { - position = 0; - options = 0; - } -}; - -// Models a policy opcode; that is a condition evaluation were all the -// arguments but one are stored in objects of this class. Use OpcodeFactory -// to create objects of this type. -// This class is just an implementation artifact and not exposed to the -// API clients or visible in the intercepted service. Internally, an -// opcode is just: -// - An integer that identifies the actual opcode. -// - An index to indicate which one is the input argument -// - An array of arguments. -// While an OO hierarchy of objects would have been a natural choice, the fact -// that 1) this code can execute before the CRT is loaded, presents serious -// problems in terms of guarantees about the actual state of the vtables and -// 2) because the opcode objects are generated in the broker process, we need to -// use plain objects. To preserve some minimal type safety templates are used -// when possible. -class PolicyOpcode { - friend class OpcodeFactory; - public: - // Evaluates the opcode. For a typical comparison opcode the return value - // is EVAL_TRUE or EVAL_FALSE. If there was an error in the evaluation the - // the return is EVAL_ERROR. If the opcode is an action opcode then the - // return can take other values such as ASK_BROKER. - // parameters: An array of all input parameters. This argument is normally - // created by the macros POLPARAMS_BEGIN() POLPARAMS_END. - // count: The number of parameters passed as first argument. - // match: The match context that is persisted across the opcode evaluation - // sequence. - EvalResult Evaluate(const ParameterSet* parameters, size_t count, - MatchContext* match); - - // Retrieves a stored argument by index. Valid index values are - // from 0 to < kArgumentCount. - template - void GetArgument(size_t index, T* argument) const { - COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size); - *argument = *reinterpret_cast(&arguments_[index].mem); - } - - // Sets a stored argument by index. Valid index values are - // from 0 to < kArgumentCount. - template - void SetArgument(size_t index, const T& argument) { - COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size); - *reinterpret_cast(&arguments_[index].mem) = argument; - } - - // Retrieves the actual address of an string argument. When using - // GetArgument() to retrieve an index that contains a string, the returned - // value is just an offset to the actual string. - // index: the stored string index. Valid values are from 0 - // to < kArgumentCount. - const wchar_t* GetRelativeString(size_t index) const { - ptrdiff_t str_delta = 0; - GetArgument(index, &str_delta); - const char* delta = reinterpret_cast(this) + str_delta; - return reinterpret_cast(delta); - } - - // Returns true if this opcode is an action opcode without actually - // evaluating it. Used to do a quick scan forward to the next opcode group. - bool IsAction() const { - return (OP_ACTION == opcode_id_); - }; - - // Returns the opcode type. - OpcodeID GetID() const { - return opcode_id_; - } - - // Returns the stored options such as kPolNegateEval and others. - uint32 GetOptions() const { - return options_; - } - - // Sets the stored options such as kPolNegateEval. - void SetOptions(int16 options) { - options_ = options; - } - - private: - - static const size_t kArgumentCount = 4; // The number of supported argument. - - struct OpcodeArgument { - UINT_PTR mem; - }; - - // Better define placement new in the class instead of relying on the - // global definition which seems to be fubared. - void* operator new(size_t, void* location) { - return location; - } - - // Helper function to evaluate the opcode. The parameters have the same - // meaning that in Evaluate(). - EvalResult EvaluateHelper(const ParameterSet* parameters, - MatchContext* match); - OpcodeID opcode_id_; - int16 parameter_; - int16 options_; - OpcodeArgument arguments_[PolicyOpcode::kArgumentCount]; -}; - -enum StringMatchOptions { - CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by - CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API. - EXACT_LENGHT = 2 // Don't do substring match. Do full string match. -}; - -// Opcodes that do string comparisons take a parameter that is the starting -// position to perform the comparison so we can do substring matching. There -// are two special values: -// -// Start from the current position and compare strings advancing forward until -// a match is found if any. Similar to CRT strstr(). -const int kSeekForward = -1; -// Perform a match with the end of the string. It only does a single comparison. -const int kSeekToEnd = 0xfffff; - - -// A PolicyBuffer is a variable size structure that contains all the opcodes -// that are to be created or evaluated in sequence. -struct PolicyBuffer { - size_t opcode_count; - PolicyOpcode opcodes[1]; -}; - -// Helper class to create any opcode sequence. This class is normally invoked -// only by the high level policy module or when you need to handcraft a special -// policy. -// The factory works by creating the opcodes using a chunk of memory given -// in the constructor. The opcodes themselves are allocated from the beginning -// (top) of the memory, while any string that an opcode needs is allocated from -// the end (bottom) of the memory. -// -// In essence: -// -// low address ---> [opcode 1] -// [opcode 2] -// [opcode 3] -// | | <--- memory_top_ -// | free | -// | | -// | | <--- memory_bottom_ -// [string 1] -// high address --> [string 2] -// -// Note that this class does not keep track of the number of opcodes made and -// it is designed to be a building block for low-level policy. -// -// Note that any of the MakeOpXXXXX member functions below can return NULL on -// failure. When that happens opcode sequence creation must be aborted. -class OpcodeFactory { - public: - // memory: base pointer to a chunk of memory where the opcodes are created. - // memory_size: the size in bytes of the memory chunk. - OpcodeFactory(char* memory, size_t memory_size) - : memory_top_(memory) { - memory_bottom_ = &memory_top_[memory_size]; - } - - // policy: contains the raw memory where the opcodes are created. - // memory_size: contains the actual size of the policy argument. - OpcodeFactory(PolicyBuffer* policy, size_t memory_size) { - memory_top_ = reinterpret_cast(&policy->opcodes[0]); - memory_bottom_ = &memory_top_[memory_size]; - } - - // Creates an OpAlwaysFalse opcode. - PolicyOpcode* MakeOpAlwaysFalse(uint32 options); - - // Creates an OpAlwaysFalse opcode. - PolicyOpcode* MakeOpAlwaysTrue(uint32 options); - - // Creates an OpAction opcode. - // action: The action to return when Evaluate() is called. - PolicyOpcode* MakeOpAction(EvalResult action, uint32 options); - - // Creates an OpNumberMatch opcode. - // selected_param: index of the input argument. It must be a ulong or the - // evaluation result will generate a EVAL_ERROR. - // match: the number to compare against the selected_param. - PolicyOpcode* MakeOpNumberMatch(int16 selected_param, unsigned long match, - uint32 options); - - // Creates an OpNumberMatch opcode (void pointers are cast to numbers). - // selected_param: index of the input argument. It must be an void* or the - // evaluation result will generate a EVAL_ERROR. - // match: the pointer numeric value to compare against selected_param. - PolicyOpcode* MakeOpVoidPtrMatch(int16 selected_param, const void* match, - uint32 options); - - // Creates an OpUlongMatchRange opcode using the memory passed in the ctor. - // selected_param: index of the input argument. It must be a ulong or the - // evaluation result will generate a EVAL_ERROR. - // lower_bound, upper_bound: the range to compare against selected_param. - PolicyOpcode* MakeOpUlongMatchRange(int16 selected_param, - unsigned long lower_bound, - unsigned long upper_bound, - uint32 options); - - // Creates an OpWStringMatch opcode using the raw memory passed in the ctor. - // selected_param: index of the input argument. It must be a wide string - // pointer or the evaluation result will generate a EVAL_ERROR. - // match_str: string to compare against selected_param. - // start_position: when its value is from 0 to < 0x7fff it indicates an - // offset from the selected_param string where to perform the comparison. If - // the value is SeekForward then a substring search is performed. If the - // value is SeekToEnd the comparison is performed against the last part of - // the selected_param string. - // Note that the range in the position (0 to 0x7fff) is dictated by the - // current implementation. - // match_opts: Indicates additional matching flags. Currently CaseInsensitive - // is supported. - PolicyOpcode* MakeOpWStringMatch(int16 selected_param, - const wchar_t* match_str, - int start_position, - StringMatchOptions match_opts, - uint32 options); - - // Creates an OpUlongAndMatch opcode using the raw memory passed in the ctor. - // selected_param: index of the input argument. It must be ulong or the - // evaluation result will generate a EVAL_ERROR. - // match: the value to bitwise AND against selected_param. - PolicyOpcode* MakeOpUlongAndMatch(int16 selected_param, - unsigned long match, - uint32 options); - - private: - // Constructs the common part of every opcode. selected_param is the index - // of the input param to use when evaluating the opcode. Pass -1 in - // selected_param to indicate that no input parameter is required. - PolicyOpcode* MakeBase(OpcodeID opcode_id, uint32 options, - int16 selected_param); - - // Allocates (and copies) a string (of size length) inside the buffer and - // returns the displacement with respect to start. - ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t lenght); - - // Returns the available memory to make opcodes. - size_t memory_size() const { - return memory_bottom_ - memory_top_; - } - - // Points to the lowest currently available address of the memory - // used to make the opcodes. This pointer increments as opcodes are made. - char* memory_top_; - - // Points to the highest currently available address of the memory - // used to make the opcodes. This pointer decrements as opcode strings are - // allocated. - char* memory_bottom_; - - DISALLOW_COPY_AND_ASSIGN(OpcodeFactory); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_POLICY_ENGINE_OPCODES_H__ diff --git a/sandbox/src/policy_engine_params.h b/sandbox/src/policy_engine_params.h deleted file mode 100644 index ecf454f..0000000 --- a/sandbox/src/policy_engine_params.h +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__ -#define SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__ - -#include "base/basictypes.h" -#include "sandbox/src/internal_types.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_nt_util.h" - -// This header defines the classes that allow the low level policy to select -// the input parameters. In order to better make sense of this header is -// recommended that you check policy_engine_opcodes.h first. - -namespace sandbox { - -// Models the set of interesting parameters of an intercepted system call -// normally you don't create objects of this class directly, instead you -// use the POLPARAMS_XXX macros. -// For example, if an intercepted function has the following signature: -// -// NTSTATUS NtOpenFileFunction (PHANDLE FileHandle, -// ACCESS_MASK DesiredAccess, -// POBJECT_ATTRIBUTES ObjectAttributes, -// PIO_STATUS_BLOCK IoStatusBlock, -// ULONG ShareAccess, -// ULONG OpenOptions); -// -// You could say that the following parameters are of interest to policy: -// -// POLPARAMS_BEGIN(open_params) -// POLPARAM(DESIRED_ACCESS) -// POLPARAM(OBJECT_NAME) -// POLPARAM(SECURITY_DESCRIPTOR) -// POLPARAM(IO_STATUS) -// POLPARAM(OPEN_OPTIONS) -// POLPARAMS_END; -// -// and the actual code will use this for defining the parameters: -// -// CountedParameterSet p; -// p[open_params::DESIRED_ACCESS] = ParamPickerMake(DesiredAccess); -// p[open_params::OBJECT_NAME] = -// ParamPickerMake(ObjectAttributes->ObjectName); -// p[open_params::SECURITY_DESCRIPTOR] = -// ParamPickerMake(ObjectAttributes->SecurityDescriptor); -// p[open_params::IO_STATUS] = ParamPickerMake(IoStatusBlock); -// p[open_params::OPEN_OPTIONS] = ParamPickerMake(OpenOptions); -// -// These will create an stack-allocated array of ParameterSet objects which -// have each 1) the address of the parameter 2) a numeric id that encodes the -// original C++ type. This allows the policy to treat any set of supported -// argument types uniformily and with some type safety. -// -// TODO(cpu): support not fully implemented yet for unicode string and will -// probably add other types as well. -class ParameterSet { - public: - ParameterSet() : real_type_(INVALID_TYPE), address_(NULL) {} - - // Retrieve the stored parameter. If the type does not match ulong fail. - bool Get(unsigned long* destination) const { - if (ULONG_TYPE != real_type_) { - return false; - } - *destination = Void2TypePointerCopy(); - return true; - } - - // Retrieve the stored parameter. If the type does not match void* fail. - bool Get(const void** destination) const { - if (VOIDPTR_TYPE != real_type_) { - return false; - } - *destination = Void2TypePointerCopy(); - return true; - } - - // Retrieve the stored parameter. If the type does not match wchar_t* fail. - bool Get(const wchar_t** destination) const { - if (WCHAR_TYPE != real_type_) { - return false; - } - *destination = Void2TypePointerCopy(); - return true; - } - - // False if the parameter is not properly initialized. - bool IsValid() const { - return INVALID_TYPE != real_type_; - } - - protected: - // The constructor can only be called by derived types, which should - // safely provide the real_type and the address of the argument. - ParameterSet(ArgType real_type, const void* address) - : real_type_(real_type), address_(address) { - } - - private: - // This template provides the same functionality as bits_cast but - // it works with pointer while the former works only with references. - template - T Void2TypePointerCopy() const { - return *(reinterpret_cast(address_)); - } - - ArgType real_type_; - const void* address_; -}; - -// To safely infer the type, we use a set of template specializations -// in ParameterSetEx with a template function ParamPickerMake to do the -// parameter type deduction. - -// Base template class. Not implemented so using unsupported types should -// fail to compile. -template -class ParameterSetEx : public ParameterSet { - public: - ParameterSetEx(const void* address); -}; - -template<> -class ParameterSetEx : public ParameterSet { - public: - ParameterSetEx(const void* address) - : ParameterSet(VOIDPTR_TYPE, address) {} -}; - -template<> -class ParameterSetEx : public ParameterSet { - public: - ParameterSetEx(const void* address) - : ParameterSet(VOIDPTR_TYPE, address) {} -}; - - -template<> -class ParameterSetEx : public ParameterSet { - public: - ParameterSetEx(const void* address) - : ParameterSet(WCHAR_TYPE, address) {} -}; - -template<> -class ParameterSetEx : public ParameterSet { - public: - ParameterSetEx(const void* address) - : ParameterSet(WCHAR_TYPE, address) {} -}; - - -template<> -class ParameterSetEx : public ParameterSet { - public: - ParameterSetEx(const void* address) - : ParameterSet(ULONG_TYPE, address) {} -}; - -template<> -class ParameterSetEx : public ParameterSet { - public: - ParameterSetEx(const void* address) - : ParameterSet(UNISTR_TYPE, address) {} -}; - -template -ParameterSet ParamPickerMake(T& parameter) { - return ParameterSetEx(¶meter); -}; - -struct CountedParameterSetBase { - int count; - ParameterSet parameters[1]; -}; - -// This template defines the actual list of policy parameters for a given -// interception. -// Warning: This template stores the address to the actual variables, in -// other words, the values are not copied. -template -struct CountedParameterSet { - CountedParameterSet() : count(T::PolParamLast) {} - - ParameterSet& operator[](typename T::Args n) { - return parameters[n]; - } - - CountedParameterSetBase* GetBase() { - return reinterpret_cast(this); - } - - int count; - ParameterSet parameters[T::PolParamLast]; -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__ diff --git a/sandbox/src/policy_engine_processor.cc b/sandbox/src/policy_engine_processor.cc deleted file mode 100644 index d0455d0..0000000 --- a/sandbox/src/policy_engine_processor.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/policy_engine_processor.h" - -namespace sandbox { - -void PolicyProcessor::SetInternalState(size_t index, EvalResult result) { - state_.current_index_ = index; - state_.current_result_ = result; -} - -EvalResult PolicyProcessor::GetAction() const { - return state_.current_result_; -} - -// Decides if an opcode can be skipped (not evaluated) or not. The function -// takes as inputs the opcode and the current evaluation context and returns -// true if the opcode should be skipped or not and also can set keep_skipping -// to false to signal that the current instruction should be skipped but not -// the next after the current one. -bool SkipOpcode(PolicyOpcode& opcode, MatchContext* context, - bool* keep_skipping) { - if (opcode.IsAction()) { - uint32 options = context->options; - context->Clear(); - *keep_skipping = false; - return (kPolUseOREval == options)? false : true; - } - *keep_skipping = true; - return true; -} - -PolicyResult PolicyProcessor::Evaluate(uint32 options, - ParameterSet* parameters, - size_t param_count) { - if (NULL == policy_) { - return NO_POLICY_MATCH; - } - if (0 == policy_->opcode_count) { - return NO_POLICY_MATCH; - } - if (!(kShortEval & options)) { - return POLICY_ERROR; - } - - MatchContext context; - bool evaluation = false; - bool skip_group = false; - SetInternalState(0, EVAL_FALSE); - size_t count = policy_->opcode_count; - - // Loop over all the opcodes Evaluating in sequence. Since we only support - // short circuit evaluation, we stop as soon as we find an 'action' opcode - // and the current evaluation is true. - // - // Skipping opcodes can happen when we are in AND mode (!kPolUseOREval) and - // have got EVAL_FALSE or when we are in OR mode (kPolUseOREval) and got - // EVAL_TRUE. Skipping will stop at the next action opcode or at the opcode - // after the action depending on kPolUseOREval. - - for (size_t ix = 0; ix != count; ++ix) { - PolicyOpcode& opcode = policy_->opcodes[ix]; - // Skipping block. - if (skip_group) { - if (SkipOpcode(opcode, &context, &skip_group)) { - continue; - } - } - // Evaluation block. - EvalResult result = opcode.Evaluate(parameters, param_count, &context); - switch (result) { - case EVAL_FALSE: - evaluation = false; - if (kPolUseOREval != context.options) { - skip_group = true; - } - break; - case EVAL_ERROR: - if (kStopOnErrors & options) { - return POLICY_ERROR; - } - break; - case EVAL_TRUE: - evaluation = true; - if (kPolUseOREval == context.options) { - skip_group = true; - } - break; - default: - // We have evaluated an action. - SetInternalState(ix, result); - return POLICY_MATCH; - } - } - - if (evaluation) { - // Reaching the end of the policy with a positive evaluation is probably - // an error: we did not find a final action opcode? - return POLICY_ERROR; - } - return NO_POLICY_MATCH; -} - - -} // namespace sandbox diff --git a/sandbox/src/policy_engine_processor.h b/sandbox/src/policy_engine_processor.h deleted file mode 100644 index 3327e36..0000000 --- a/sandbox/src/policy_engine_processor.h +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__ -#define SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__ - -#include "base/basictypes.h" -#include "sandbox/src/policy_engine_params.h" -#include "sandbox/src/policy_engine_opcodes.h" - -namespace sandbox { - -// This header contains the core policy evaluator. In its simplest form -// it evaluates a stream of opcodes assuming that they are laid out in -// memory as opcode groups. -// -// An opcode group has N comparison opcodes plus 1 action opcode. For -// example here we have 3 opcode groups (A, B,C): -// -// [comparison 1] <-- group A start -// [comparison 2] -// [comparison 3] -// [action A ] -// [comparison 1] <-- group B start -// [action B ] -// [comparison 1] <-- group C start -// [comparison 2] -// [action C ] -// -// The opcode evaluator proceeds from the top, evaluating each opcode in -// sequence. An opcode group is evaluated until the first comparison that -// returns false. At that point the rest of the group is skipped and evaluation -// resumes with the first comparison of the next group. When all the comparisons -// in a group have evaluated to true and the action is reached. The group is -// considered a matching group. -// -// In the 'ShortEval' mode evaluation stops when it reaches the end or the first -// matching group. The action opcode from this group is the resulting policy -// action. -// -// In the 'RankedEval' mode evaluation stops only when it reaches the end of the -// the opcode stream. In the process all matching groups are saved and at the -// end the 'best' group is selected (what makes the best is TBD) and the action -// from this group is the resulting policy action. -// -// As explained above, the policy evaluation of a group is a logical AND of -// the evaluation of each opcode. However an opcode can request kPolUseOREval -// which makes the evaluation to use logical OR. Given that each opcode can -// request its evaluation result to be negated with kPolNegateEval you can -// achieve the negation of the total group evaluation. This means that if you -// need to express: -// if (!(c1 && c2 && c3)) -// You can do it by: -// if ((!c1) || (!c2) || (!c3)) -// - -// Possible outcomes of policy evaluation. -enum PolicyResult { - NO_POLICY_MATCH, - POLICY_MATCH, - POLICY_ERROR -}; - -// Policy evaluation flags -// TODO(cpu): implement the options 0 & 4. -// -// Stop evaluating as soon as an error is encountered. -const uint32 kStopOnErrors = 0; -// Ignore all non fatal opcode evaluation errors. -const uint32 kIgnoreErrors = 1; -// Short-circuit evaluation: Only evaluate until opcode group that -// evaluated to true has been found. -const uint32 kShortEval = 2; -// Discussed briefly at the policy design meeting. It will evaluate -// all rules and then return the 'best' rule that evaluated true. -const uint32 kRankedEval = 4; - -// This class evaluates a policy-opcode stream given the memory where the -// opcodes are and an input 'parameter set'. -// -// This class is designed to be callable from interception points -// as low as the NtXXXX service level (it is not currently safe, but -// it is designed to be made safe). -// -// Its usage in an interception is: -// -// POLPARAMS_BEGIN(eval_params) -// POLPARAM(param1) -// POLPARAM(param2) -// POLPARAM(param3) -// POLPARAM(param4) -// POLPARAM(param5) -// POLPARAMS_END; -// -// PolicyProcessor pol_evaluator(policy_memory); -// PolicyResult pr = pol_evaluator.Evaluate(ShortEval, eval_params, -// _countof(eval_params)); -// if (NO_POLICY_MATCH == pr) { -// EvalResult policy_action = pol_evaluator.GetAction(); -// // apply policy here... -// } -// -// Where the POLPARAM() arguments are derived from the intercepted function -// arguments, and represent all the 'interesting' policy inputs, and -// policy_memory is a memory buffer containing the opcode stream that is the -// relevant policy for this intercept. -class PolicyProcessor { - public: - // policy_buffer contains opcodes made with OpcodeFactory. They are usually - // created in the broker process and evaluated in the target process. - - // This constructor is just a variant of the previous constructor. - explicit PolicyProcessor(PolicyBuffer* policy) - : policy_(policy) { - SetInternalState(0, EVAL_FALSE); - } - - // Evaluates a policy-opcode stream. See the comments at the top of this - // class for more info. Returns POLICY_MATCH if a rule set was found that - // matches an active policy. - PolicyResult Evaluate(uint32 options, - ParameterSet* parameters, - size_t parameter_count); - - // If the result of Evaluate() was POLICY_MATCH, calling this function returns - // the recommended policy action. - EvalResult GetAction() const; - - private: - struct { - size_t current_index_; - EvalResult current_result_; - } state_; - - // Sets the currently matching action result. - void SetInternalState(size_t index, EvalResult result); - - PolicyBuffer* policy_; - DISALLOW_COPY_AND_ASSIGN(PolicyProcessor); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__ diff --git a/sandbox/src/policy_engine_unittest.cc b/sandbox/src/policy_engine_unittest.cc deleted file mode 100644 index f988ccc..0000000 --- a/sandbox/src/policy_engine_unittest.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/policy_engine_params.h" -#include "sandbox/src/policy_engine_processor.h" -#include "testing/gtest/include/gtest/gtest.h" - -#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = { -#define POLPARAM(p) sandbox::ParamPickerMake(p), -#define POLPARAMS_END } - -namespace sandbox { - -bool SetupNtdllImports(); - -TEST(PolicyEngineTest, Rules1) { - SetupNtdllImports(); - - // Construct two policy rules that say: - // - // #1 - // If the path is c:\\documents and settings\\* AND - // If the creation mode is 'open existing' AND - // If the security descriptor is null THEN - // Ask the broker. - // - // #2 - // If the security descriptor is null AND - // If the path ends with *.txt AND - // If the creation mode is not 'create new' THEN - // return Access Denied. - - enum FileCreateArgs { - FileNameArg, - CreationDispositionArg, - FlagsAndAttributesArg, - SecurityAttributes - }; - - const size_t policy_sz = 1024; - PolicyBuffer* policy = reinterpret_cast(new char[policy_sz]); - OpcodeFactory opcode_maker(policy, policy_sz - 0x40); - - // Add rule set #1 - opcode_maker.MakeOpWStringMatch(FileNameArg, - L"c:\\documents and settings\\", - 0, CASE_INSENSITIVE, kPolNone); - opcode_maker.MakeOpNumberMatch(CreationDispositionArg, OPEN_EXISTING, - kPolNone); - opcode_maker.MakeOpVoidPtrMatch(SecurityAttributes, (void*)NULL, - kPolNone); - opcode_maker.MakeOpAction(ASK_BROKER, kPolNone); - - // Add rule set #2 - opcode_maker.MakeOpWStringMatch(FileNameArg, L".TXT", - kSeekToEnd, CASE_INSENSITIVE, kPolNone); - opcode_maker.MakeOpNumberMatch(CreationDispositionArg, CREATE_NEW, - kPolNegateEval); - opcode_maker.MakeOpAction(FAKE_ACCESS_DENIED, kPolNone); - policy->opcode_count = 7; - - wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt"; - unsigned long creation_mode = OPEN_EXISTING; - unsigned long flags = FILE_ATTRIBUTE_NORMAL; - void* security_descriptor = NULL; - - POLPARAMS_BEGIN(eval_params) - POLPARAM(filename) - POLPARAM(creation_mode) - POLPARAM(flags) - POLPARAM(security_descriptor) - POLPARAMS_END; - - PolicyResult pr; - PolicyProcessor pol_ev(policy); - - // Test should match the first rule set. - pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, pr); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - // Test should still match the first rule set. - pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, pr); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - // Changing creation_mode such that evaluation should not match any rule. - creation_mode = CREATE_NEW; - pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, pr); - - // Changing creation_mode such that evaluation should match rule #2. - creation_mode = OPEN_ALWAYS; - pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, pr); - EXPECT_EQ(FAKE_ACCESS_DENIED, pol_ev.GetAction()); - - delete [] reinterpret_cast(policy); -} - -} // namespace sandbox diff --git a/sandbox/src/policy_low_level.cc b/sandbox/src/policy_low_level.cc deleted file mode 100644 index 401f2e1..0000000 --- a/sandbox/src/policy_low_level.cc +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "sandbox/src/policy_low_level.h" -#include "base/basictypes.h" - -namespace { - - // A single rule can use at most this amount of memory. - const size_t kRuleBufferSize = 1024*4; - - // The possible states of the string matching opcode generator. - enum { - PENDING_NONE, - PENDING_ASTERISK, // Have seen an '*' but have not generated an opcode. - PENDING_QMARK, // Have seen an '?' but have not generated an opcode. - }; - - // The category of the last character seen by the string matching opcode - // generator. - const uint32 kLastCharIsNone = 0; - const uint32 kLastCharIsAlpha = 1; - const uint32 kLastCharIsWild = 2; - const uint32 kLastCharIsAsterisk = kLastCharIsWild + 4; - const uint32 kLastCharIsQuestionM = kLastCharIsWild + 8; -} - -namespace sandbox { - -// Adding a rule is nothing more than pushing it into an stl container. Done() -// is called for the rule in case the code that made the rule in the first -// place has not done it. -bool LowLevelPolicy::AddRule(int service, PolicyRule* rule) { - if (!rule->Done()) { - return false; - } - - PolicyRule* local_rule = new PolicyRule(*rule); - RuleNode node = {local_rule, service}; - rules_.push_back(node); - return true; -} - -LowLevelPolicy::~LowLevelPolicy() { - // Delete all the rules. - typedef std::list RuleNodes; - for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { - delete it->rule; - } -} - -// Here is where the heavy byte shuffling is done. We take all the rules and -// 'compile' them into a single memory region. Now, the rules are in random -// order so the first step is to reorganize them into a stl map that is keyed -// by the service id and as a value contains a list with all the rules that -// belong to that service. Then we enter the big for-loop where we carve a -// memory zone for the opcodes and the data and call RebindCopy on each rule -// so they all end up nicely packed in the policy_store_. -bool LowLevelPolicy::Done() { - typedef std::list RuleNodes; - typedef std::list RuleList; - typedef std::map Mmap; - Mmap mmap; - - for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { - mmap[it->service].push_back(it->rule); - } - - PolicyBuffer* current_buffer = &policy_store_->data[0]; - char* buffer_end = reinterpret_cast(current_buffer) + - policy_store_->data_size; - size_t avail_size = policy_store_->data_size; - - for (Mmap::iterator it = mmap.begin(); it != mmap.end(); ++it) { - uint32 service = (*it).first; - if (service >= kMaxServiceCount) { - return false; - } - policy_store_->entry[service] = current_buffer; - - RuleList::iterator rules_it = (*it).second.begin(); - RuleList::iterator rules_it_end = (*it).second.end(); - - size_t svc_opcode_count = 0; - - for (; rules_it != rules_it_end; ++rules_it) { - const PolicyRule* rule = (*rules_it); - size_t op_count = rule->GetOpcodeCount(); - - size_t opcodes_size = op_count * sizeof(PolicyOpcode); - if (avail_size < opcodes_size) { - return false; - } - size_t data_size = avail_size - opcodes_size; - PolicyOpcode* opcodes_start = ¤t_buffer->opcodes[svc_opcode_count]; - if (!rule->RebindCopy(opcodes_start, opcodes_size, - buffer_end, &data_size)) { - return false; - } - size_t used = avail_size - data_size; - buffer_end -= used; - avail_size -= used; - svc_opcode_count += op_count; - } - - current_buffer->opcode_count += svc_opcode_count; - size_t policy_byte_count = (svc_opcode_count * sizeof(PolicyOpcode)) - / sizeof(current_buffer[0]); - current_buffer = ¤t_buffer[policy_byte_count + 1]; - } - - return true; -} - -PolicyRule::PolicyRule(EvalResult action) - : action_(action), done_(false) { - char* memory = new char[sizeof(PolicyBuffer) + kRuleBufferSize]; - buffer_ = reinterpret_cast(memory); - buffer_->opcode_count = 0; - opcode_factory_ = new OpcodeFactory(buffer_, - kRuleBufferSize + sizeof(PolicyOpcode)); -} - -PolicyRule::PolicyRule(const PolicyRule& other) { - if (this == &other) - return; - action_ = other.action_; - done_ = other.done_; - size_t buffer_size = sizeof(PolicyBuffer) + kRuleBufferSize; - char* memory = new char[buffer_size]; - buffer_ = reinterpret_cast(memory); - memcpy(buffer_, other.buffer_, buffer_size); - - char* opcode_buffer = reinterpret_cast(&buffer_->opcodes[0]); - char* buffer_end = &opcode_buffer[kRuleBufferSize + sizeof(PolicyOpcode)]; - char* next_opcode = &opcode_buffer[GetOpcodeCount() * sizeof(PolicyOpcode)]; - opcode_factory_ = new OpcodeFactory(next_opcode, buffer_end - next_opcode); -} - -// This function get called from a simple state machine implemented in -// AddStringMatch() which passes the current state (in state) and it passes -// true in last_call if AddStringMatch() has finished processing the input -// pattern string and this would be the last call to generate any pending -// opcode. The skip_count is the currently accumulated number of '?' seen so -// far and once the associated opcode is generated this function sets it back -// to zero. -bool PolicyRule::GenStringOpcode(RuleType rule_type, - StringMatchOptions match_opts, - uint16 parameter, int state, bool last_call, - int* skip_count, std::wstring* fragment) { - - // The last opcode must: - // 1) Always clear the context. - // 2) Preserve the negation. - // 3) Remove the 'OR' mode flag. - uint32 options = kPolNone; - if (last_call) { - if (IF_NOT == rule_type) { - options = kPolClearContext | kPolNegateEval; - } else { - options = kPolClearContext; - } - } else if (IF_NOT == rule_type) { - options = kPolUseOREval | kPolNegateEval; - } - - PolicyOpcode* op = NULL; - - // The fragment string contains the accumulated characters to match with, it - // never contains wildcards (unless they have been escaped) and while there - // is no fragment there is no new string match opcode to generate. - if (fragment->empty()) { - // There is no new opcode to generate but in the last call we have to fix - // the previous opcode because it was really the last but we did not know - // it at that time. - if (last_call && (buffer_->opcode_count > 0)) { - op = &buffer_->opcodes[buffer_->opcode_count - 1]; - op->SetOptions(options); - } - return true; - } - - if (PENDING_ASTERISK == state) { - if (last_call) { - op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), - kSeekToEnd, match_opts, - options); - } else { - op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), - kSeekForward, match_opts, - options); - } - - } else if (PENDING_QMARK == state) { - op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), - *skip_count, match_opts, options); - *skip_count = 0; - } else { - if (last_call) { - match_opts = static_cast(EXACT_LENGHT | match_opts); - } - op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 0, - match_opts, options); - } - if (NULL == op) { - return false; - } - ++buffer_->opcode_count; - fragment->clear(); - return true; -} - -bool PolicyRule::AddStringMatch(RuleType rule_type, int16 parameter, - const wchar_t* string, - StringMatchOptions match_opts) { - if (done_) { - // Do not allow to add more rules after generating the action opcode. - return false; - } - - const wchar_t* current_char = string; - uint32 last_char = kLastCharIsNone; - int state = PENDING_NONE; - int skip_count = 0; // counts how many '?' we have seen in a row. - std::wstring fragment; // accumulates the non-wildcard part of the string. - - while (L'\0' != *current_char) { - switch (*current_char) { - case L'*': - if (kLastCharIsWild & last_char) { - // '**' and '&*' is an error. - return false; - } - if (!GenStringOpcode(rule_type, match_opts, parameter, - state, false, &skip_count, &fragment)) { - return false; - } - last_char = kLastCharIsAsterisk; - state = PENDING_ASTERISK; - break; - case L'?': - if (kLastCharIsAsterisk == last_char) { - // '*?' is an error. - return false; - } - if (!GenStringOpcode(rule_type, match_opts, parameter, - state, false, &skip_count, &fragment)) { - return false; - } - ++skip_count; - last_char = kLastCharIsQuestionM; - state = PENDING_QMARK; - break; - case L'/': - // Note: "/?" is an escaped '?'. Eat the slash and fall through. - if (L'?' == current_char[1]) { - ++current_char; - } - default: - fragment += *current_char; - last_char = kLastCharIsAlpha; - } - ++current_char; - } - - if (!GenStringOpcode(rule_type, match_opts, parameter, - state, true, &skip_count, &fragment)) { - return false; - } - return true; -} - -bool PolicyRule::AddNumberMatch(RuleType rule_type, int16 parameter, - unsigned long number, RuleOp comparison_op) { - if (done_) { - // Do not allow to add more rules after generating the action opcode. - return false; - } - uint32 opts = (rule_type == IF_NOT)? kPolNegateEval : kPolNone; - - if (EQUAL == comparison_op) { - if (NULL == opcode_factory_->MakeOpNumberMatch(parameter, number, opts)) { - return false; - } - } else if (AND == comparison_op) { - if (NULL == opcode_factory_->MakeOpUlongAndMatch(parameter, number, opts)) { - return false; - } - } - ++buffer_->opcode_count; - return true; -} - -bool PolicyRule::Done() { - if (done_) { - return true; - } - if (NULL == opcode_factory_->MakeOpAction(action_, kPolNone)) { - return false; - } - ++buffer_->opcode_count; - done_ = true; - return true; -} - -bool PolicyRule::RebindCopy(PolicyOpcode* opcode_start, size_t opcode_size, - char* data_start, size_t* data_size) const { - size_t count = buffer_->opcode_count; - for (size_t ix = 0; ix != count; ++ix) { - if (opcode_size < sizeof(PolicyOpcode)) { - return false; - } - PolicyOpcode& opcode = buffer_->opcodes[ix]; - *opcode_start = opcode; - if (OP_WSTRING_MATCH == opcode.GetID()) { - // For this opcode argument 0 is a delta to the string and argument 1 - // is the length (in chars) of the string. - const wchar_t* str = opcode.GetRelativeString(0); - size_t str_len; - opcode.GetArgument(1, &str_len); - str_len = str_len * sizeof(wchar_t); - if ((*data_size) < str_len) { - return false; - } - *data_size -= str_len; - data_start -= str_len; - memcpy(data_start, str, str_len); - // Recompute the string displacement - ptrdiff_t delta = data_start - reinterpret_cast(opcode_start); - opcode_start->SetArgument(0, delta); - } - ++opcode_start; - opcode_size -= sizeof(PolicyOpcode); - } - - return true; -} - -PolicyRule::~PolicyRule() { - delete [] reinterpret_cast(buffer_); - delete opcode_factory_; -} - -} // namespace sandbox diff --git a/sandbox/src/policy_low_level.h b/sandbox/src/policy_low_level.h deleted file mode 100644 index 5bf6a46..0000000 --- a/sandbox/src/policy_low_level.h +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_POLICY_LOW_LEVEL_H__ -#define SANDBOX_SRC_POLICY_LOW_LEVEL_H__ - -#include - -#include "base/basictypes.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_engine_params.h" -#include "sandbox/src/policy_engine_opcodes.h" - -// Low level policy classes. -// Built on top of the PolicyOpcode and OpcodeFatory, the low level policy -// provides a way to define rules on strings and numbers but it is unaware -// of Windows specific details or how the Interceptions must be set up. -// To use these classes you construct one or more rules and add them to the -// LowLevelPolicy object like this: -// -// PolicyRule rule1(ASK_BROKER); -// rule1.AddStringMatch(IF, 0, L"\\\\/?/?\\c:\\*Microsoft*\\*.exe", true); -// rule1.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL); -// rule1.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL); -// -// PolicyRule rule2(FAKE_SUCCESS); -// rule2.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*", false)); -// rule2.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL)); -// -// LowLevelPolicy policyGen(*policy_memory); -// policyGen.AddRule(kNtCreateFileSvc, &rule1); -// policyGen.AddRule(kNtCreateFileSvc, &rule2); -// policyGen.Done(); -// -// At this point (error checking omitted) the policy_memory can be copied -// to the target process where it can be evaluated. - -namespace sandbox { - -// TODO(cpu): Move this constant to crosscall_client.h. -const size_t kMaxServiceCount = 32; -COMPILE_ASSERT(IPC_LAST_TAG <= kMaxServiceCount, kMaxServiceCount_is_too_low); - -// Defines the memory layout of the policy. This memory is filled by -// LowLevelPolicy object. -// For example: -// -// [Service 0] --points to---\ -// [Service 1] --------------|-----\ -// ...... | | -// [Service N] | | -// [data_size] | | -// [Policy Buffer 0] <-------/ | -// [opcodes of] | -// ....... | -// [Policy Buffer 1] <-------------/ -// [opcodes] -// ....... -// ....... -// [Policy Buffer N] -// [opcodes] -// ....... -// -// ....... -// [opcode string ] -// [opcode string ] -// ....... -// [opcode string ] -struct PolicyGlobal { - PolicyBuffer* entry[kMaxServiceCount]; - size_t data_size; - PolicyBuffer data[1]; -}; - -class PolicyRule; - -// Provides the means to collect rules into a policy store (memory) -class LowLevelPolicy { - public: - // policy_store: must contain allocated memory and the internal - // size fields set to correct values. - explicit LowLevelPolicy(PolicyGlobal* policy_store) - : policy_store_(policy_store) { - } - - // Destroys all the policy rules. - ~LowLevelPolicy(); - - // Adds a rule to be generated when Done() is called. - // service: The id of the service that this rule is associated with, - // for example the 'Open Thread' service or the "Create File" service. - // returns false on error. - bool AddRule(int service, PolicyRule* rule); - - // Generates all the rules added with AddRule() into the memory area - // passed on the constructor. Returns false on error. - bool Done(); - - private: - struct RuleNode { - const PolicyRule* rule; - int service; - }; - std::list rules_; - PolicyGlobal* policy_store_; - DISALLOW_IMPLICIT_CONSTRUCTORS(LowLevelPolicy); -}; - -// There are 'if' rules and 'if not' comparisons -enum RuleType { - IF = 0, - IF_NOT = 1, -}; - -// Possible comparisons for numbers -enum RuleOp { - EQUAL, - AND, - RANGE // TODO(cpu): Implement this option. -}; - -// Provides the means to collect a set of comparisons into a single -// rule and its associated action. -class PolicyRule { - friend class LowLevelPolicy; - - public: - explicit PolicyRule(EvalResult action); - PolicyRule(const PolicyRule& other); - ~PolicyRule(); - - // Adds a string comparison to the rule. - // rule_type: possible values are IF and IF_NOT. - // parameter: the expected index of the argument for this rule. For example - // in a 'create file' service the file name argument can be at index 0. - // string: is the desired matching pattern. - // match_opts: if the pattern matching is case sensitive or not. - bool AddStringMatch(RuleType rule_type, int16 parameter, - const wchar_t* string, StringMatchOptions match_opts); - - // Adds a number match comparison to the rule. - // rule_type: possible values are IF and IF_NOT. - // parameter: the expected index of the argument for this rule. - // number: the value to compare the input to. - // comparison_op: the comparison kind (equal, logical and, etc). - bool AddNumberMatch(RuleType rule_type, int16 parameter, - unsigned long number, RuleOp comparison_op); - - // Returns the number of opcodes generated so far. - size_t GetOpcodeCount() const { - return buffer_->opcode_count; - } - - // Called when there is no more comparisons to add. Internally it generates - // the last opcode (the action opcode). Returns false if this operation fails. - bool Done(); - - private: - void operator=(const PolicyRule&); - // Called in a loop from AddStringMatch to generate the required string - // match opcodes. rule_type, match_opts and parameter are the same as - // in AddStringMatch. - bool GenStringOpcode(RuleType rule_type, StringMatchOptions match_opts, - uint16 parameter, int state, bool last_call, - int* skip_count, std::wstring* fragment); - - // Loop over all generated opcodes and copy them to increasing memory - // addresses from opcode_start and copy the extra data (strings usually) into - // decreasing addresses from data_start. Extra data is only present in the - // string evaluation opcodes. - bool RebindCopy(PolicyOpcode* opcode_start, size_t opcode_size, - char* data_start, size_t* data_size) const; - PolicyBuffer* buffer_; - OpcodeFactory* opcode_factory_; - EvalResult action_; - bool done_; -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_POLICY_LOW_LEVEL_H__ diff --git a/sandbox/src/policy_low_level_unittest.cc b/sandbox/src/policy_low_level_unittest.cc deleted file mode 100644 index 20d1777..0000000 --- a/sandbox/src/policy_low_level_unittest.cc +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/policy_engine_params.h" -#include "sandbox/src/policy_engine_processor.h" -#include "sandbox/src/policy_low_level.h" -#include "testing/gtest/include/gtest/gtest.h" - -#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = { -#define POLPARAM(p) sandbox::ParamPickerMake(p), -#define POLPARAMS_END } - -namespace sandbox { - -bool SetupNtdllImports(); - -// Testing that we allow opcode generation on valid string patterns. -TEST(PolicyEngineTest, StringPatternsOK) { - SetupNtdllImports(); - PolicyRule pr(ASK_BROKER); - EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\adobe\\ver??\\", CASE_SENSITIVE)); - EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"*.tmp", CASE_SENSITIVE)); - EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\*.doc", CASE_SENSITIVE)); - EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\windows\\*", CASE_SENSITIVE)); - EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"d:\\adobe\\acrobat.exe", - CASE_SENSITIVE)); -} - -// Testing that we signal invalid string patterns. -TEST(PolicyEngineTest, StringPatternsBAD) { - SetupNtdllImports(); - PolicyRule pr(ASK_BROKER); - EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"one**two", CASE_SENSITIVE)); - EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"**three", CASE_SENSITIVE)); - EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"five?six*?seven", CASE_SENSITIVE)); - EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"eight?*nine", CASE_SENSITIVE)); -} - -// Helper function to allocate space (on the heap) for policy. -PolicyGlobal* MakePolicyMemory() { - const size_t kTotalPolicySz = 4096*8; - char* mem = new char[kTotalPolicySz]; - memset(mem, 0, kTotalPolicySz); - PolicyGlobal* policy = reinterpret_cast(mem); - policy->data_size = kTotalPolicySz - sizeof(PolicyGlobal); - return policy; -} - -// The simplest test using LowLevelPolicy it should test a single opcode which -// does a exact string comparison. -TEST(PolicyEngineTest, SimpleStrMatch) { - SetupNtdllImports(); - PolicyRule pr(ASK_BROKER); - EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"z:\\Directory\\domo.txt", - CASE_INSENSITIVE)); - - PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 2; - - LowLevelPolicy policyGen(policy); - EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); - EXPECT_TRUE(policyGen.Done()); - - wchar_t* filename = L"Z:\\Directory\\domo.txt"; - - POLPARAMS_BEGIN(eval_params) - POLPARAM(filename) // Argument 0 - POLPARAMS_END; - - PolicyResult result; - PolicyProcessor pol_ev(policy->entry[kFakeService]); - - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - filename = L"Z:\\Directory\\domo.txt.tmp"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - delete [] reinterpret_cast(policy); -} - -TEST(PolicyEngineTest, SimpleIfNotStrMatch) { - SetupNtdllImports(); - PolicyRule pr(ASK_BROKER); - EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\", - CASE_SENSITIVE)); - - PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 2; - LowLevelPolicy policyGen(policy); - - EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); - EXPECT_TRUE(policyGen.Done()); - - wchar_t* filename = NULL; - POLPARAMS_BEGIN(eval_params) - POLPARAM(filename) // Argument 0 - POLPARAMS_END; - - PolicyResult result; - PolicyProcessor pol_ev(policy->entry[kFakeService]); - - filename = L"c:\\Microsoft\\"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\MicroNerd\\"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - filename = L"c:\\Microsoft\\domo.txt"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - delete [] reinterpret_cast(policy); -} - -TEST(PolicyEngineTest, SimpleIfNotStrMatchWild1) { - SetupNtdllImports(); - PolicyRule pr(ASK_BROKER); - EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", - CASE_SENSITIVE)); - - PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 3; - LowLevelPolicy policyGen(policy); - - EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); - EXPECT_TRUE(policyGen.Done()); - - wchar_t* filename = NULL; - POLPARAMS_BEGIN(eval_params) - POLPARAM(filename) // Argument 0 - POLPARAMS_END; - - PolicyResult result; - PolicyProcessor pol_ev(policy->entry[kFakeService]); - - filename = L"c:\\Microsoft\\domo.txt"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\MicroNerd\\domo.txt"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - delete [] reinterpret_cast(policy); -} - -TEST(PolicyEngineTest, SimpleIfNotStrMatchWild2) { - SetupNtdllImports(); - PolicyRule pr(ASK_BROKER); - EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*.txt", - CASE_SENSITIVE)); - - PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 3; - LowLevelPolicy policyGen(policy); - - EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); - EXPECT_TRUE(policyGen.Done()); - - wchar_t* filename = NULL; - POLPARAMS_BEGIN(eval_params) - POLPARAM(filename) // Argument 0 - POLPARAMS_END; - - PolicyResult result; - PolicyProcessor pol_ev(policy->entry[kFakeService]); - - filename = L"c:\\Microsoft\\domo.txt"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\MicroNerd\\domo.txt"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - filename = L"c:\\Microsoft\\domo.bmp"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - delete [] reinterpret_cast(policy); -} - -TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild1) { - SetupNtdllImports(); - PolicyRule pr(ASK_BROKER); - EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", - CASE_SENSITIVE)); - EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL)); - - PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 3; - LowLevelPolicy policyGen(policy); - - EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); - EXPECT_TRUE(policyGen.Done()); - - wchar_t* filename = NULL; - unsigned long access = 0; - POLPARAMS_BEGIN(eval_params) - POLPARAM(filename) // Argument 0 - POLPARAM(access) // Argument 1 - POLPARAMS_END; - - PolicyResult result; - PolicyProcessor pol_ev(policy->entry[kFakeService]); - - filename = L"c:\\Microsoft\\domo.txt"; - access = 24; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\Microsoft\\domo.txt"; - access = 42; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\MicroNerd\\domo.txt"; - access = 24; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - filename = L"c:\\Micronesia\\domo.txt"; - access = 42; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - delete [] reinterpret_cast(policy); -} - -TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild2) { - SetupNtdllImports(); - PolicyRule pr(ASK_BROKER); - EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL)); - EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\GoogleV?\\*.txt", - CASE_SENSITIVE)); - EXPECT_TRUE(pr.AddNumberMatch(IF, 2, 66, EQUAL)); - - PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 3; - LowLevelPolicy policyGen(policy); - - EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); - EXPECT_TRUE(policyGen.Done()); - - wchar_t* filename = NULL; - unsigned long access = 0; - unsigned long sharing = 66; - - POLPARAMS_BEGIN(eval_params) - POLPARAM(filename) // Argument 0 - POLPARAM(access) // Argument 1 - POLPARAM(sharing) // Argument 2 - POLPARAMS_END; - - PolicyResult result; - PolicyProcessor pol_ev(policy->entry[kFakeService]); - - filename = L"c:\\GoogleV2\\domo.txt"; - access = 24; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\GoogleV2\\domo.bmp"; - access = 24; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - filename = L"c:\\GoogleV23\\domo.txt"; - access = 24; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - - filename = L"c:\\GoogleV2\\domo.txt"; - access = 42; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\Google\\domo.txt"; - access = 24; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - filename = L"c:\\Micronesia\\domo.txt"; - access = 42; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\GoogleV2\\domo.bmp"; - access = 24; - sharing = 0; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - delete [] reinterpret_cast(policy); -} - -// Testing one single rule in one single service. The service is made to -// resemble NtCreateFile. -TEST(PolicyEngineTest, OneRuleTest) { - SetupNtdllImports(); - PolicyRule pr(ASK_BROKER); - EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\*Microsoft*\\*.txt", - CASE_SENSITIVE)); - EXPECT_TRUE(pr.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL)); - EXPECT_TRUE(pr.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); - - PolicyGlobal* policy = MakePolicyMemory(); - - const uint32 kNtFakeCreateFile = 7; - - LowLevelPolicy policyGen(policy); - EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr)); - EXPECT_TRUE(policyGen.Done()); - - wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt"; - unsigned long creation_mode = OPEN_EXISTING; - unsigned long flags = FILE_ATTRIBUTE_NORMAL; - void* security_descriptor = NULL; - - POLPARAMS_BEGIN(eval_params) - POLPARAM(filename) // Argument 0 - POLPARAM(creation_mode) // Argument 1 - POLPARAM(flags) // Argument 2 - POLPARAM(security_descriptor) - POLPARAMS_END; - - PolicyResult result; - PolicyProcessor pol_ev(policy->entry[kNtFakeCreateFile]); - - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - creation_mode = CREATE_ALWAYS; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - creation_mode = OPEN_EXISTING; - filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt.tmp"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - flags = FILE_ATTRIBUTE_DEVICE; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\Other\\Macrosoft\\Another file.txt"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\Microsoft\\1.txt"; - flags = FILE_ATTRIBUTE_NORMAL; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); - - filename = L"c:\\Microsoft\\1.ttt"; - result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - delete [] reinterpret_cast(policy); -} - -// Testing 3 rules in 3 services. Two of the services resemble File services. -TEST(PolicyEngineTest, ThreeRulesTest) { - SetupNtdllImports(); - PolicyRule pr_pipe(FAKE_SUCCESS); - EXPECT_TRUE(pr_pipe.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*", - CASE_INSENSITIVE)); - EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL)); - EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); - - size_t opc1 = pr_pipe.GetOpcodeCount(); - EXPECT_EQ(3, opc1); - - PolicyRule pr_dump(ASK_BROKER); - EXPECT_TRUE(pr_dump.AddStringMatch(IF, 0, L"\\\\/?/?\\*\\Crash Reports\\*", - CASE_INSENSITIVE)); - EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 1, CREATE_ALWAYS, EQUAL)); - EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); - - size_t opc2 = pr_dump.GetOpcodeCount(); - EXPECT_EQ(4, opc2); - - PolicyRule pr_winexe(SIGNAL_ALARM); - EXPECT_TRUE(pr_winexe.AddStringMatch(IF, 0, L"\\\\/?/?\\C:\\Windows\\*.exe", - CASE_INSENSITIVE)); - EXPECT_TRUE(pr_winexe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); - - size_t opc3 = pr_winexe.GetOpcodeCount(); - EXPECT_EQ(3, opc3); - - PolicyRule pr_adobe(GIVE_CACHED); - EXPECT_TRUE(pr_adobe.AddStringMatch(IF, 0, L"c:\\adobe\\ver?.?\\", - CASE_SENSITIVE)); - EXPECT_TRUE(pr_adobe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); - - size_t opc4 = pr_adobe.GetOpcodeCount(); - EXPECT_EQ(4, opc4); - - PolicyRule pr_none(GIVE_FIRST); - EXPECT_TRUE(pr_none.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_READONLY, AND)); - EXPECT_TRUE(pr_none.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_SYSTEM, AND)); - - size_t opc5 = pr_none.GetOpcodeCount(); - EXPECT_EQ(2, opc5); - - PolicyGlobal* policy = MakePolicyMemory(); - - const uint32 kNtFakeNone = 4; - const uint32 kNtFakeCreateFile = 5; - const uint32 kNtFakeOpenFile = 6; - - LowLevelPolicy policyGen(policy); - EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_pipe)); - EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_dump)); - EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_winexe)); - - EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_adobe)); - EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_pipe)); - - EXPECT_TRUE(policyGen.AddRule(kNtFakeNone, &pr_none)); - - EXPECT_TRUE(policyGen.Done()); - - // Inspect the policy structure manually. - EXPECT_TRUE(NULL == policy->entry[0]); - EXPECT_TRUE(NULL == policy->entry[1]); - EXPECT_TRUE(NULL == policy->entry[2]); - EXPECT_TRUE(NULL == policy->entry[3]); - EXPECT_TRUE(NULL != policy->entry[4]); // kNtFakeNone. - EXPECT_TRUE(NULL != policy->entry[5]); // kNtFakeCreateFile. - EXPECT_TRUE(NULL != policy->entry[6]); // kNtFakeOpenFile. - EXPECT_TRUE(NULL == policy->entry[7]); - - // The total per service opcode counts now must take in account one - // extra opcode (action opcode) per rule. - ++opc1; - ++opc2; - ++opc3; - ++opc4; - ++opc5; - - size_t tc1 = policy->entry[kNtFakeNone]->opcode_count; - size_t tc2 = policy->entry[kNtFakeCreateFile]->opcode_count; - size_t tc3 = policy->entry[kNtFakeOpenFile]->opcode_count; - - EXPECT_EQ(opc5, tc1); - EXPECT_EQ((opc1 + opc2 + opc3), tc2); - EXPECT_EQ((opc1 + opc4), tc3); - - // Check the type of the first and last opcode of each service. - - EXPECT_EQ(OP_ULONG_AND_MATCH, policy->entry[kNtFakeNone]->opcodes[0].GetID()); - EXPECT_EQ(OP_ACTION, policy->entry[kNtFakeNone]->opcodes[tc1-1].GetID()); - EXPECT_EQ(OP_WSTRING_MATCH, - policy->entry[kNtFakeCreateFile]->opcodes[0].GetID()); - EXPECT_EQ(OP_ACTION, - policy->entry[kNtFakeCreateFile]->opcodes[tc2-1].GetID()); - EXPECT_EQ(OP_WSTRING_MATCH, - policy->entry[kNtFakeOpenFile]->opcodes[0].GetID()); - EXPECT_EQ(OP_ACTION, policy->entry[kNtFakeOpenFile]->opcodes[tc3-1].GetID()); - - // Test the policy evaluation. - - wchar_t* filename = L""; - unsigned long creation_mode = OPEN_EXISTING; - unsigned long flags = FILE_ATTRIBUTE_NORMAL; - void* security_descriptor = NULL; - - POLPARAMS_BEGIN(params) - POLPARAM(filename) // Argument 0 - POLPARAM(creation_mode) // Argument 1 - POLPARAM(flags) // Argument 2 - POLPARAM(security_descriptor) - POLPARAMS_END; - - PolicyResult result; - PolicyProcessor eval_CreateFile(policy->entry[kNtFakeCreateFile]); - PolicyProcessor eval_OpenFile(policy->entry[kNtFakeOpenFile]); - PolicyProcessor eval_None(policy->entry[kNtFakeNone]); - - result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_None.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"\\\\??\\c:\\Windows\\System32\\calc.exe"; - flags = FILE_ATTRIBUTE_SYSTEM; - result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_None.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - flags += FILE_ATTRIBUTE_READONLY; - result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_None.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(GIVE_FIRST, eval_None.GetAction()); - result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - flags = FILE_ATTRIBUTE_NORMAL; - result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(SIGNAL_ALARM, eval_CreateFile.GetAction()); - result = eval_None.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"c:\\adobe\\ver3.2\\temp"; - result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_None.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(GIVE_CACHED, eval_OpenFile.GetAction()); - - filename = L"c:\\adobe\\ver3.22\\temp"; - result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"\\\\??\\c:\\some path\\other path\\crash reports\\some path"; - creation_mode = CREATE_ALWAYS; - result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(ASK_BROKER, eval_CreateFile.GetAction()); - result = eval_None.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - - filename = L"\\\\??\\Pipe\\Chrome.12345"; - creation_mode = OPEN_EXISTING; - result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(FAKE_SUCCESS, eval_CreateFile.GetAction()); - result = eval_None.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(NO_POLICY_MATCH, result); - result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); - EXPECT_EQ(POLICY_MATCH, result); - EXPECT_EQ(FAKE_SUCCESS, eval_OpenFile.GetAction()); - - delete [] reinterpret_cast(policy); -} - -} // namespace sandbox diff --git a/sandbox/src/policy_opcodes_unittest.cc b/sandbox/src/policy_opcodes_unittest.cc deleted file mode 100644 index 37ccd80..0000000 --- a/sandbox/src/policy_opcodes_unittest.cc +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/sandbox_nt_types.h" -#include "sandbox/src/policy_engine_params.h" -#include "sandbox/src/policy_engine_opcodes.h" -#include "testing/gtest/include/gtest/gtest.h" - - -#define INIT_GLOBAL_RTL(member) \ - g_nt.##member = reinterpret_cast<##member##Function>( \ - ::GetProcAddress(ntdll, #member)); \ - if (NULL == g_nt.##member) \ - return false - -namespace sandbox { - -SANDBOX_INTERCEPT NtExports g_nt; - -bool SetupNtdllImports() { - HMODULE ntdll = ::GetModuleHandle(kNtdllName); - - INIT_GLOBAL_RTL(RtlAllocateHeap); - INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString); - INIT_GLOBAL_RTL(RtlCompareUnicodeString); - INIT_GLOBAL_RTL(RtlCreateHeap); - INIT_GLOBAL_RTL(RtlDestroyHeap); - INIT_GLOBAL_RTL(RtlFreeHeap); - INIT_GLOBAL_RTL(_strnicmp); - INIT_GLOBAL_RTL(strlen); - INIT_GLOBAL_RTL(wcslen); - - return true; -} - -TEST(PolicyEngineTest, ParameterSetTest) { - void* pv1 = reinterpret_cast(0x477EAA5); - const void* pv2 = reinterpret_cast(0x987654); - ParameterSet pset1 = ParamPickerMake(pv1); - ParameterSet pset2 = ParamPickerMake(pv2); - - // Test that we can store and retrieve a void pointer: - const void* result1 =0; - unsigned long result2 = 0; - EXPECT_TRUE(pset1.Get(&result1)); - EXPECT_TRUE(pv1 == result1); - EXPECT_FALSE(pset1.Get(&result2)); - EXPECT_TRUE(pset2.Get(&result1)); - EXPECT_TRUE(pv2 == result1); - EXPECT_FALSE(pset2.Get(&result2)); - - // Test that we can store and retrieve a ulong: - unsigned long number = 12747; - ParameterSet pset3 = ParamPickerMake(number); - EXPECT_FALSE(pset3.Get(&result1)); - EXPECT_TRUE(pset3.Get(&result2)); - EXPECT_EQ(number, result2); - - // Test that we can store and retrieve a string: - const wchar_t* txt = L"S231L"; - ParameterSet pset4 = ParamPickerMake(txt); - const wchar_t* result3 = NULL; - EXPECT_TRUE(pset4.Get(&result3)); - EXPECT_EQ(0, wcscmp(txt, result3)); -} - -TEST(PolicyEngineTest, OpcodeConstraints) { - // Test that PolicyOpcode has no virtual functions - // because these objects are copied over to other processes - // so they cannot have vtables. - EXPECT_FALSE(__is_polymorphic(PolicyOpcode)); - // Keep developers from adding smarts to the opcodes which should - // be pretty much a bag of bytes with a OO interface. - EXPECT_TRUE(__has_trivial_destructor(PolicyOpcode)); - EXPECT_TRUE(__has_trivial_constructor(PolicyOpcode)); - EXPECT_TRUE(__has_trivial_copy(PolicyOpcode)); -} - -TEST(PolicyEngineTest, TrueFalseOpcodes) { - void* dummy = NULL; - ParameterSet ppb1 = ParamPickerMake(dummy); - char memory[1024]; - OpcodeFactory opcode_maker(memory, sizeof(memory)); - - // This opcode always evaluates to true. - PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone); - EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&ppb1, 1, NULL)); - EXPECT_FALSE(op1->IsAction()); - - // This opcode always evaluates to false. - PolicyOpcode* op2 = opcode_maker.MakeOpAlwaysTrue(kPolNone); - EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL)); - - // Nulls not allowed on the params. - EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 0, NULL)); - EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 1, NULL)); - - // True and False opcodes do not 'require' a number of parameters - EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 0, NULL)); - EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL)); - - // Test Inverting the logic. Note that inversion is done outside - // any particular opcode evaluation so no need to repeat for all - // opcodes. - PolicyOpcode* op3 = opcode_maker.MakeOpAlwaysFalse(kPolNegateEval); - EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&ppb1, 1, NULL)); - PolicyOpcode* op4 = opcode_maker.MakeOpAlwaysTrue(kPolNegateEval); - EXPECT_EQ(EVAL_FALSE, op4->Evaluate(&ppb1, 1, NULL)); - - // Test that we clear the match context - PolicyOpcode* op5 = opcode_maker.MakeOpAlwaysTrue(kPolClearContext); - MatchContext context; - context.position = 1; - context.options = kPolUseOREval; - EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&ppb1, 1, &context)); - EXPECT_EQ(0, context.position); - MatchContext context2; - EXPECT_EQ(context2.options, context.options); -} - -TEST(PolicyEngineTest, OpcodeMakerCase1) { - // Testing that the opcode maker does not overrun the - // supplied buffer. It should only be able to make 'count' opcodes. - void* dummy = NULL; - ParameterSet ppb1 = ParamPickerMake(dummy); - - char memory[256]; - OpcodeFactory opcode_maker(memory, sizeof(memory)); - size_t count = sizeof(memory) / sizeof(PolicyOpcode); - - for (size_t ix =0; ix != count; ++ix) { - PolicyOpcode* op = opcode_maker.MakeOpAlwaysFalse(kPolNone); - ASSERT_TRUE(NULL != op); - EXPECT_EQ(EVAL_FALSE, op->Evaluate(&ppb1, 1, NULL)); - } - // There should be no room more another opcode: - PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone); - ASSERT_TRUE(NULL == op1); -} - -TEST(PolicyEngineTest, OpcodeMakerCase2) { - SetupNtdllImports(); - // Testing that the opcode maker does not overrun the - // supplied buffer. It should only be able to make 'count' opcodes. - // The difference with the previous test is that this opcodes allocate - // the string 'txt2' inside the same buffer. - const wchar_t* txt1 = L"1234"; - const wchar_t txt2[] = L"123"; - - ParameterSet ppb1 = ParamPickerMake(txt1); - MatchContext mc1; - - char memory[256]; - OpcodeFactory opcode_maker(memory, sizeof(memory)); - size_t count = sizeof(memory) / (sizeof(PolicyOpcode) + sizeof(txt2)); - - // Test that it does not overrun the buffer. - for (size_t ix =0; ix != count; ++ix) { - PolicyOpcode* op = opcode_maker.MakeOpWStringMatch(0, txt2, 0, - CASE_SENSITIVE, - kPolClearContext); - ASSERT_TRUE(NULL != op); - EXPECT_EQ(EVAL_TRUE, op->Evaluate(&ppb1, 1, &mc1)); - } - - // There should be no room more another opcode: - PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0, - CASE_SENSITIVE, - kPolNone); - ASSERT_TRUE(NULL == op1); -} - -TEST(PolicyEngineTest, IntegerOpcodes) { - const wchar_t* txt = L"abcdef"; - unsigned long num1 = 42; - unsigned long num2 = 113377; - - ParameterSet pp_wrong1 = ParamPickerMake(txt); - ParameterSet pp_num1 = ParamPickerMake(num1); - ParameterSet pp_num2 = ParamPickerMake(num2); - - char memory[128]; - OpcodeFactory opcode_maker(memory, sizeof(memory)); - - // Test basic match for unsigned longs 42 == 42 and 42 != 113377. - PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, unsigned long(42), - kPolNone); - EXPECT_EQ(EVAL_TRUE, op_m42->Evaluate(&pp_num1, 1, NULL)); - EXPECT_EQ(EVAL_FALSE, op_m42->Evaluate(&pp_num2, 1, NULL)); - EXPECT_EQ(EVAL_ERROR, op_m42->Evaluate(&pp_wrong1, 1, NULL)); - - // Test basic match for void pointers. - const void* vp = NULL; - ParameterSet pp_num3 = ParamPickerMake(vp); - PolicyOpcode* op_vp_null = opcode_maker.MakeOpVoidPtrMatch(0, NULL, - kPolNone); - EXPECT_EQ(EVAL_TRUE, op_vp_null->Evaluate(&pp_num3, 1, NULL)); - EXPECT_EQ(EVAL_FALSE, op_vp_null->Evaluate(&pp_num1, 1, NULL)); - EXPECT_EQ(EVAL_ERROR, op_vp_null->Evaluate(&pp_wrong1, 1, NULL)); - - // Basic range test [41 43] (inclusive). - PolicyOpcode* op_range1 = opcode_maker.MakeOpUlongMatchRange(0, 41, 43, - kPolNone); - EXPECT_EQ(EVAL_TRUE, op_range1->Evaluate(&pp_num1, 1, NULL)); - EXPECT_EQ(EVAL_FALSE, op_range1->Evaluate(&pp_num2, 1, NULL)); - EXPECT_EQ(EVAL_ERROR, op_range1->Evaluate(&pp_wrong1, 1, NULL)); -} - -TEST(PolicyEngineTest, LogicalOpcodes) { - char memory[128]; - OpcodeFactory opcode_maker(memory, sizeof(memory)); - - unsigned long num1 = 0x10100702; - ParameterSet pp_num1 = ParamPickerMake(num1); - - PolicyOpcode* op_and1 = opcode_maker.MakeOpUlongAndMatch(0, 0x00100000, - kPolNone); - EXPECT_EQ(EVAL_TRUE, op_and1->Evaluate(&pp_num1, 1, NULL)); - PolicyOpcode* op_and2 = opcode_maker.MakeOpUlongAndMatch(0, 0x00000001, - kPolNone); - EXPECT_EQ(EVAL_FALSE, op_and2->Evaluate(&pp_num1, 1, NULL)); -} - -TEST(PolicyEngineTest, WCharOpcodes1) { - SetupNtdllImports(); - - const wchar_t* txt1 = L"the quick fox jumps over the lazy dog"; - const wchar_t txt2[] = L"the quick"; - const wchar_t txt3[] = L" fox jumps"; - const wchar_t txt4[] = L"the lazy dog"; - const wchar_t txt5[] = L"jumps over"; - const wchar_t txt6[] = L"g"; - - ParameterSet pp_tc1 = ParamPickerMake(txt1); - char memory[512]; - OpcodeFactory opcode_maker(memory, sizeof(memory)); - - PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0, - CASE_SENSITIVE, - kPolNone); - - // Simplest substring match from pos 0. It should be a successful match - // and the match context should be updated. - MatchContext mc1; - EXPECT_EQ(EVAL_TRUE, op1->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_TRUE(_countof(txt2) == mc1.position + 1); - - // Matching again should fail and the context should be unmodified. - EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_TRUE(_countof(txt2) == mc1.position + 1); - - // Using the same match context we should continue where we left - // in the previous successful match, - PolicyOpcode* op3 = opcode_maker.MakeOpWStringMatch(0, txt3, 0, - CASE_SENSITIVE, - kPolNone); - EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_TRUE(_countof(txt3) + _countof(txt2) == mc1.position + 2); - - // We now keep on matching but now we skip 6 characters which means - // we skip the string ' over '. And we zero the match context. This is - // the primitive that we use to build '??'. - PolicyOpcode* op4 = opcode_maker.MakeOpWStringMatch(0, txt4, 6, - CASE_SENSITIVE, - kPolClearContext); - EXPECT_EQ(EVAL_TRUE, op4->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_EQ(0, mc1.position); - - // Test that we can properly match the last part of the string - PolicyOpcode* op4b = opcode_maker.MakeOpWStringMatch(0, txt4, kSeekToEnd, - CASE_SENSITIVE, - kPolClearContext); - EXPECT_EQ(EVAL_TRUE, op4b->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_EQ(0, mc1.position); - - // Test matching 'jumps over' over the entire string. This is the - // primitive we build '*' from. - PolicyOpcode* op5 = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekForward, - CASE_SENSITIVE, kPolNone); - EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_EQ(24, mc1.position); - - // Test that we don't match because it is not at the end of the string - PolicyOpcode* op5b = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekToEnd, - CASE_SENSITIVE, - kPolNone); - EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1)); - - // Test that we function if the string does not fit. In this case we - // try to match 'the lazy dog' against 'he lazy dog'. - PolicyOpcode* op6 = opcode_maker.MakeOpWStringMatch(0, txt4, 2, - CASE_SENSITIVE, kPolNone); - EXPECT_EQ(24, mc1.position); - - // Testing matching against 'g' which should be the last char. - MatchContext mc2; - PolicyOpcode* op7 = opcode_maker.MakeOpWStringMatch(0, txt6, kSeekForward, - CASE_SENSITIVE, kPolNone); - EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2)); - - // Trying to match again should fail since we are in the last char. - // This also covers a couple of boundary conditions. - EXPECT_EQ(EVAL_FALSE, op7->Evaluate(&pp_tc1, 1, &mc2)); -} - -TEST(PolicyEngineTest, WCharOpcodes2) { - SetupNtdllImports(); - - const wchar_t* path1 = L"c:\\documents and settings\\Microsoft\\BLAH.txt"; - const wchar_t txt1[] = L"Settings\\microsoft"; - ParameterSet pp_tc1 = ParamPickerMake(path1); - - char memory[256]; - OpcodeFactory opcode_maker(memory, sizeof(memory)); - MatchContext mc1; - - // Testing case-insensitive does not buy us much since it this option - // is just passed to the Microsoft API that we use normally, but just for - // coverage, here it is: - PolicyOpcode* op1s = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward, - CASE_SENSITIVE, kPolNone); - PolicyOpcode* op1i = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward, - CASE_INSENSITIVE, - kPolNone); - EXPECT_EQ(EVAL_FALSE, op1s->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_EQ(EVAL_TRUE, op1i->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_EQ(35, mc1.position); -} - -TEST(PolicyEngineTest, ActionOpcodes) { - char memory[256]; - OpcodeFactory opcode_maker(memory, sizeof(memory)); - MatchContext mc1; - void* dummy = NULL; - ParameterSet ppb1 = ParamPickerMake(dummy); - - PolicyOpcode* op1 = opcode_maker.MakeOpAction(ASK_BROKER, kPolNone); - EXPECT_TRUE(op1->IsAction()); - EXPECT_EQ(ASK_BROKER, op1->Evaluate(&ppb1, 1, &mc1)); -} - -} // namespace sandbox diff --git a/sandbox/src/policy_params.h b/sandbox/src/policy_params.h deleted file mode 100644 index e1fb3fc..0000000 --- a/sandbox/src/policy_params.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_POLICY_PARAMS_H__ -#define SANDBOX_SRC_POLICY_PARAMS_H__ - -#include "sandbox/src/policy_engine_params.h" - -namespace sandbox { - -class ParameterSet; - -// Warning: The following macros store the address to the actual variables, in -// other words, the values are not copied. -#define POLPARAMS_BEGIN(type) class type { public: enum Args { -#define POLPARAM(arg) arg, -#define POLPARAMS_END(type) PolParamLast }; }; \ - typedef sandbox::ParameterSet type##Array [type::PolParamLast]; - -// Policy parameters for file open / create. -POLPARAMS_BEGIN(OpenFile) - POLPARAM(NAME) - POLPARAM(BROKER) // TRUE if called from the broker. - POLPARAM(ACCESS) - POLPARAM(OPTIONS) -POLPARAMS_END(OpenFile) - -// Policy parameter for name-based policies. -POLPARAMS_BEGIN(FileName) - POLPARAM(NAME) - POLPARAM(BROKER) // TRUE if called from the broker. -POLPARAMS_END(FileName) - -COMPILE_ASSERT(OpenFile::NAME == FileName::NAME, to_simplify_fs_policies); -COMPILE_ASSERT(OpenFile::BROKER == FileName::BROKER, to_simplify_fs_policies); - -// Policy parameter for name-based policies. -POLPARAMS_BEGIN(NameBased) - POLPARAM(NAME) -POLPARAMS_END(NameBased) - -// Policy parameters for open event. -POLPARAMS_BEGIN(OpenEventParams) - POLPARAM(NAME) - POLPARAM(ACCESS) -POLPARAMS_END(OpenEventParams) - -// Policy Parameters for reg open / create. -POLPARAMS_BEGIN(OpenKey) - POLPARAM(NAME) - POLPARAM(ACCESS) -POLPARAMS_END(OpenKey) - -// Policy parameter for name-based policies. -POLPARAMS_BEGIN(HandleTarget) - POLPARAM(NAME) - POLPARAM(TARGET) -POLPARAMS_END(HandleTarget) - - -} // namespace sandbox - -#endif // SANDBOX_SRC_POLICY_PARAMS_H__ diff --git a/sandbox/src/policy_target.cc b/sandbox/src/policy_target.cc deleted file mode 100644 index aa69892..0000000 --- a/sandbox/src/policy_target.cc +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/policy_target.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_engine_processor.h" -#include "sandbox/src/policy_low_level.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/target_services.h" - -namespace sandbox { - -// Handle for our private heap. -extern void* g_heap; - -// This is the list of all imported symbols from ntdll.dll. -SANDBOX_INTERCEPT NtExports g_nt; - -// Policy data. -extern void* volatile g_shared_policy_memory; -SANDBOX_INTERCEPT size_t g_shared_policy_size; - -bool QueryBroker(int ipc_id, CountedParameterSetBase* params) { - DCHECK_NT(static_cast(ipc_id) < kMaxServiceCount); - DCHECK_NT(g_shared_policy_memory); - DCHECK_NT(g_shared_policy_size > 0); - - if (static_cast(ipc_id) >= kMaxServiceCount) - return false; - - PolicyGlobal* global_policy = - reinterpret_cast(g_shared_policy_memory); - - if (!global_policy->entry[ipc_id]) - return false; - - PolicyBuffer* policy = reinterpret_cast( - reinterpret_cast(g_shared_policy_memory) + - reinterpret_cast(global_policy->entry[ipc_id])); - - if ((reinterpret_cast(global_policy->entry[ipc_id]) > - global_policy->data_size) || - (g_shared_policy_size < global_policy->data_size)) { - NOTREACHED_NT(); - return false; - } - - for (int i = 0; i < params->count; i++) { - if (!params->parameters[i].IsValid()) { - NOTREACHED_NT(); - return false; - } - } - - PolicyProcessor processor(policy); - PolicyResult result = processor.Evaluate(kShortEval, params->parameters, - params->count); - DCHECK_NT(POLICY_ERROR != result); - - return POLICY_MATCH == result && ASK_BROKER == processor.GetAction(); -} - -// ----------------------------------------------------------------------- - -// Hooks NtSetInformationThread to block RevertToSelf from being -// called before the actual call to LowerToken. -NTSTATUS WINAPI TargetNtSetInformationThread( - NtSetInformationThreadFunction orig_SetInformationThread, HANDLE thread, - NT_THREAD_INFORMATION_CLASS thread_info_class, PVOID thread_information, - ULONG thread_information_bytes) { - do { - if (SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) - break; - if (ThreadImpersonationToken != thread_info_class) - break; - if (!thread_information) - break; - HANDLE token; - if (sizeof(token) > thread_information_bytes) - break; - - NTSTATUS ret = CopyData(&token, thread_information, sizeof(token)); - if (!NT_SUCCESS(ret) || NULL != token) - break; - - // This is a revert to self. - return STATUS_SUCCESS; - } while (false); - - return orig_SetInformationThread(thread, thread_info_class, - thread_information, - thread_information_bytes); -} - -// Hooks NtOpenThreadToken to force the open_as_self parameter to be set to -// FALSE if we are still running with the impersonation token. open_as_self set -// to TRUE means that the token will be open using the process token instead of -// the impersonation token. This is bad because the process token does not have -// access to open the thread token. -NTSTATUS WINAPI TargetNtOpenThreadToken( - NtOpenThreadTokenFunction orig_OpenThreadToken, HANDLE thread, - ACCESS_MASK desired_access, BOOLEAN open_as_self, PHANDLE token) { - if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) - open_as_self = FALSE; - - return orig_OpenThreadToken(thread, desired_access, open_as_self, token); -} - -// See comment for TargetNtOpenThreadToken -NTSTATUS WINAPI TargetNtOpenThreadTokenEx( - NtOpenThreadTokenExFunction orig_OpenThreadTokenEx, HANDLE thread, - ACCESS_MASK desired_access, BOOLEAN open_as_self, ULONG handle_attributes, - PHANDLE token) { - if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) - open_as_self = FALSE; - - return orig_OpenThreadTokenEx(thread, desired_access, open_as_self, - handle_attributes, token); -} - -} // namespace sandbox diff --git a/sandbox/src/policy_target.h b/sandbox/src/policy_target.h deleted file mode 100644 index 4a77b47..0000000 --- a/sandbox/src/policy_target.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -#ifndef SANDBOX_SRC_POLICY_TARGET_H__ -#define SANDBOX_SRC_POLICY_TARGET_H__ - -namespace sandbox { - -struct CountedParameterSetBase; - -// Performs a policy lookup and returns true if the request should be passed to -// the broker process. -bool QueryBroker(int ipc_id, CountedParameterSetBase* params); - -extern "C" { - -// Interception of NtSetInformationThread on the child process. -// It should never be called directly. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationThread( - NtSetInformationThreadFunction orig_SetInformationThread, HANDLE thread, - NT_THREAD_INFORMATION_CLASS thread_info_class, PVOID thread_information, - ULONG thread_information_bytes); - -// Interception of NtOpenThreadToken on the child process. -// It should never be called directly -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadToken( - NtOpenThreadTokenFunction orig_OpenThreadToken, HANDLE thread, - ACCESS_MASK desired_access, BOOLEAN open_as_self, PHANDLE token); - -// Interception of NtOpenThreadTokenEx on the child process. -// It should never be called directly -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadTokenEx( - NtOpenThreadTokenExFunction orig_OpenThreadTokenEx, HANDLE thread, - ACCESS_MASK desired_access, BOOLEAN open_as_self, ULONG handle_attributes, - PHANDLE token); - -} // extern "C" - -} // namespace sandbox - -#endif // SANDBOX_SRC_POLICY_TARGET_H__ diff --git a/sandbox/src/policy_target_test.cc b/sandbox/src/policy_target_test.cc deleted file mode 100644 index aef7548..0000000 --- a/sandbox/src/policy_target_test.cc +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/win/scoped_process_information.h" -#include "base/win/windows_version.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_utils.h" -#include "sandbox/src/target_services.h" -#include "sandbox/tests/common/controller.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -#define BINDNTDLL(name) \ - name ## Function name = reinterpret_cast( \ - ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) - -// Reverts to self and verify that SetInformationToken was faked. Returns -// SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked. -SBOX_TESTS_COMMAND int PolicyTargetTest_token(int argc, wchar_t **argv) { - HANDLE thread_token; - // Get the thread token, using impersonation. - if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | - TOKEN_DUPLICATE, FALSE, &thread_token)) - return ::GetLastError(); - - ::RevertToSelf(); - ::CloseHandle(thread_token); - - int ret = SBOX_TEST_FAILED; - if (::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, - FALSE, &thread_token)) { - ret = SBOX_TEST_SUCCEEDED; - ::CloseHandle(thread_token); - } - return ret; -} - -// Stores the high privilege token on a static variable, change impersonation -// again to that one and verify that we are not interfering anymore with -// RevertToSelf. -SBOX_TESTS_COMMAND int PolicyTargetTest_steal(int argc, wchar_t **argv) { - static HANDLE thread_token; - if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) { - if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | - TOKEN_DUPLICATE, FALSE, &thread_token)) - return ::GetLastError(); - } else { - if (!::SetThreadToken(NULL, thread_token)) - return ::GetLastError(); - - // See if we fake the call again. - int ret = PolicyTargetTest_token(argc, argv); - ::CloseHandle(thread_token); - return ret; - } - return 0; -} - -// Opens the thread token with and without impersonation. -SBOX_TESTS_COMMAND int PolicyTargetTest_token2(int argc, wchar_t **argv) { - HANDLE thread_token; - // Get the thread token, using impersonation. - if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | - TOKEN_DUPLICATE, FALSE, &thread_token)) - return ::GetLastError(); - ::CloseHandle(thread_token); - - // Get the thread token, without impersonation. - if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, - TRUE, &thread_token)) - return ::GetLastError(); - ::CloseHandle(thread_token); - return SBOX_TEST_SUCCEEDED; -} - -// Opens the thread token with and without impersonation, using -// NtOpenThreadTokenEX. -SBOX_TESTS_COMMAND int PolicyTargetTest_token3(int argc, wchar_t **argv) { - BINDNTDLL(NtOpenThreadTokenEx); - if (!NtOpenThreadTokenEx) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - HANDLE thread_token; - // Get the thread token, using impersonation. - NTSTATUS status = NtOpenThreadTokenEx(GetCurrentThread(), - TOKEN_IMPERSONATE | TOKEN_DUPLICATE, - FALSE, 0, &thread_token); - if (status == STATUS_NO_TOKEN) - return ERROR_NO_TOKEN; - if (!NT_SUCCESS(status)) - return SBOX_TEST_FAILED; - - ::CloseHandle(thread_token); - - // Get the thread token, without impersonation. - status = NtOpenThreadTokenEx(GetCurrentThread(), - TOKEN_IMPERSONATE | TOKEN_DUPLICATE, TRUE, 0, - &thread_token); - if (!NT_SUCCESS(status)) - return SBOX_TEST_FAILED; - - ::CloseHandle(thread_token); - return SBOX_TEST_SUCCEEDED; -} - -// Tests that we can open the current thread. -SBOX_TESTS_COMMAND int PolicyTargetTest_thread(int argc, wchar_t **argv) { - DWORD thread_id = ::GetCurrentThreadId(); - HANDLE thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id); - if (!thread) - return ::GetLastError(); - if (!::CloseHandle(thread)) - return ::GetLastError(); - - return SBOX_TEST_SUCCEEDED; -} - -// New thread entry point: do nothing. -DWORD WINAPI PolicyTargetTest_thread_main(void* param) { - ::Sleep(INFINITE); - return 0; -} - -// Tests that we can create a new thread, and open it. -SBOX_TESTS_COMMAND int PolicyTargetTest_thread2(int argc, wchar_t **argv) { - // Use default values to create a new thread. - DWORD thread_id; - HANDLE thread = ::CreateThread(NULL, 0, &PolicyTargetTest_thread_main, 0, 0, - &thread_id); - if (!thread) - return ::GetLastError(); - if (!::CloseHandle(thread)) - return ::GetLastError(); - - thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id); - if (!thread) - return ::GetLastError(); - - if (!::CloseHandle(thread)) - return ::GetLastError(); - - return SBOX_TEST_SUCCEEDED; -} - -// Tests that we can call CreateProcess. -SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t **argv) { - // Use default values to create a new process. - STARTUPINFO startup_info = {0}; - startup_info.cb = sizeof(startup_info); - base::win::ScopedProcessInformation process_info; - if (!::CreateProcessW(L"foo.exe", L"foo.exe", NULL, NULL, FALSE, 0, - NULL, NULL, &startup_info, process_info.Receive())) - return SBOX_TEST_SUCCEEDED; - return SBOX_TEST_FAILED; -} - -TEST(PolicyTargetTest, SetInformationThread) { - TestRunner runner; - if (base::win::GetVersion() >= base::win::VERSION_XP) { - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token")); - } - - runner.SetTestState(AFTER_REVERT); - EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token")); - - runner.SetTestState(EVERY_STATE); - if (base::win::GetVersion() >= base::win::VERSION_XP) - EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal")); -} - -TEST(PolicyTargetTest, OpenThreadToken) { - TestRunner runner; - if (base::win::GetVersion() >= base::win::VERSION_XP) { - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2")); - } - - runner.SetTestState(AFTER_REVERT); - EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2")); -} - -TEST(PolicyTargetTest, OpenThreadTokenEx) { - TestRunner runner; - if (base::win::GetVersion() < base::win::VERSION_XP) - return; - - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token3")); - - runner.SetTestState(AFTER_REVERT); - EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token3")); -} - -TEST(PolicyTargetTest, OpenThread) { - TestRunner runner; - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread")) << - "Opens the current thread"; - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread2")) << - "Creates a new thread and opens it"; -} - -TEST(PolicyTargetTest, OpenProcess) { - TestRunner runner; - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_process")) << - "Opens a process"; -} - -// Launches the app in the sandbox and ask it to wait in an -// infinite loop. Waits for 2 seconds and then check if the -// desktop associated with the app thread is not the same as the -// current desktop. -TEST(PolicyTargetTest, DesktopPolicy) { - BrokerServices* broker = GetBroker(); - - // Precreate the desktop. - TargetPolicy* temp_policy = broker->CreatePolicy(); - temp_policy->CreateAlternateDesktop(false); - temp_policy->Release(); - - ASSERT_TRUE(broker != NULL); - - // Get the path to the sandboxed app. - wchar_t prog_name[MAX_PATH]; - GetModuleFileNameW(NULL, prog_name, MAX_PATH); - - std::wstring arguments(L"\""); - arguments += prog_name; - arguments += L"\" -child 0 wait"; // Don't care about the "state" argument. - - // Launch the app. - ResultCode result = SBOX_ALL_OK; - base::win::ScopedProcessInformation target; - - TargetPolicy* policy = broker->CreatePolicy(); - policy->SetAlternateDesktop(false); - policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); - result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, - target.Receive()); - policy->Release(); - - EXPECT_EQ(SBOX_ALL_OK, result); - - EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); - - EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000)); - - EXPECT_NE(::GetThreadDesktop(target.thread_id()), - ::GetThreadDesktop(::GetCurrentThreadId())); - - std::wstring desktop_name = policy->GetAlternateDesktop(); - HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); - EXPECT_TRUE(NULL != desk); - EXPECT_TRUE(::CloseDesktop(desk)); - EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); - - ::WaitForSingleObject(target.process_handle(), INFINITE); - - // Close the desktop handle. - temp_policy = broker->CreatePolicy(); - temp_policy->DestroyAlternateDesktop(); - temp_policy->Release(); - - // Make sure the desktop does not exist anymore. - desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); - EXPECT_TRUE(NULL == desk); -} - -// Launches the app in the sandbox and ask it to wait in an -// infinite loop. Waits for 2 seconds and then check if the -// winstation associated with the app thread is not the same as the -// current desktop. -TEST(PolicyTargetTest, WinstaPolicy) { - BrokerServices* broker = GetBroker(); - - // Precreate the desktop. - TargetPolicy* temp_policy = broker->CreatePolicy(); - temp_policy->CreateAlternateDesktop(true); - temp_policy->Release(); - - ASSERT_TRUE(broker != NULL); - - // Get the path to the sandboxed app. - wchar_t prog_name[MAX_PATH]; - GetModuleFileNameW(NULL, prog_name, MAX_PATH); - - std::wstring arguments(L"\""); - arguments += prog_name; - arguments += L"\" -child 0 wait"; // Don't care about the "state" argument. - - // Launch the app. - ResultCode result = SBOX_ALL_OK; - base::win::ScopedProcessInformation target; - - TargetPolicy* policy = broker->CreatePolicy(); - policy->SetAlternateDesktop(true); - policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); - result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, - target.Receive()); - policy->Release(); - - EXPECT_EQ(SBOX_ALL_OK, result); - - EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); - - EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000)); - - EXPECT_NE(::GetThreadDesktop(target.thread_id()), - ::GetThreadDesktop(::GetCurrentThreadId())); - - std::wstring desktop_name = policy->GetAlternateDesktop(); - ASSERT_FALSE(desktop_name.empty()); - - // Make sure there is a backslash, for the window station name. - EXPECT_NE(desktop_name.find_first_of(L'\\'), std::wstring::npos); - - // Isolate the desktop name. - desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1); - - HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); - // This should fail if the desktop is really on another window station. - EXPECT_FALSE(NULL != desk); - EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); - - ::WaitForSingleObject(target.process_handle(), INFINITE); - - // Close the desktop handle. - temp_policy = broker->CreatePolicy(); - temp_policy->DestroyAlternateDesktop(); - temp_policy->Release(); -} - -} // namespace sandbox diff --git a/sandbox/src/process_policy_test.cc b/sandbox/src/process_policy_test.cc deleted file mode 100644 index 783446e..0000000 --- a/sandbox/src/process_policy_test.cc +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "base/sys_string_conversions.h" -#include "base/win/scoped_handle.h" -#include "base/win/scoped_process_information.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/tests/common/controller.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -// While the shell API provides better calls than this home brew function -// we use GetSystemWindowsDirectoryW which does not query the registry so -// it is safe to use after revert. -std::wstring MakeFullPathToSystem32(const wchar_t* name) { - wchar_t windows_path[MAX_PATH] = {0}; - ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH); - std::wstring full_path(windows_path); - if (full_path.empty()) { - return full_path; - } - full_path += L"\\system32\\"; - full_path += name; - return full_path; -} - -// Creates a process with the |exe| and |command| parameter using the -// unicode and ascii version of the api. -sandbox::SboxTestResult CreateProcessHelper(const std::wstring &exe, - const std::wstring &command) { - base::win::ScopedProcessInformation pi; - STARTUPINFOW si = {sizeof(si)}; - - const wchar_t *exe_name = NULL; - if (!exe.empty()) - exe_name = exe.c_str(); - - const wchar_t *cmd_line = NULL; - if (!command.empty()) - cmd_line = command.c_str(); - - // Create the process with the unicode version of the API. - sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED; - if (!::CreateProcessW(exe_name, const_cast(cmd_line), NULL, NULL, - FALSE, 0, NULL, NULL, &si, pi.Receive())) { - DWORD last_error = GetLastError(); - if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || - (ERROR_ACCESS_DENIED == last_error) || - (ERROR_FILE_NOT_FOUND == last_error)) { - ret1 = sandbox::SBOX_TEST_DENIED; - } else { - ret1 = sandbox::SBOX_TEST_FAILED; - } - } else { - ret1 = sandbox::SBOX_TEST_SUCCEEDED; - } - - pi.Close(); - - // Do the same with the ansi version of the api - STARTUPINFOA sia = {sizeof(sia)}; - sandbox::SboxTestResult ret2 = sandbox::SBOX_TEST_FAILED; - - std::string narrow_cmd_line; - if (cmd_line) - narrow_cmd_line = base::SysWideToMultiByte(cmd_line, CP_UTF8); - if (!::CreateProcessA( - exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str() : NULL, - cmd_line ? const_cast(narrow_cmd_line.c_str()) : NULL, - NULL, NULL, FALSE, 0, NULL, NULL, &sia, pi.Receive())) { - DWORD last_error = GetLastError(); - if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || - (ERROR_ACCESS_DENIED == last_error) || - (ERROR_FILE_NOT_FOUND == last_error)) { - ret2 = sandbox::SBOX_TEST_DENIED; - } else { - ret2 = sandbox::SBOX_TEST_FAILED; - } - } else { - ret2 = sandbox::SBOX_TEST_SUCCEEDED; - } - - if (ret1 == ret2) - return ret1; - - return sandbox::SBOX_TEST_FAILED; -} - -} // namespace - -namespace sandbox { - -// Tries to create the process in argv[0] using 7 different ways. -// Since we also try the Ansi and Unicode version of the CreateProcess API, -// The process referenced by argv[0] will be spawned 14 times. -SBOX_TESTS_COMMAND int Process_RunApp(int argc, wchar_t **argv) { - if (argc != 1) { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - if ((NULL == argv) || (NULL == argv[0])) { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - std::wstring path = MakeFullPathToSystem32(argv[0]); - - // TEST 1: Try with the path in the app_name. - int result1 = CreateProcessHelper(path, std::wstring()); - - // TEST 2: Try with the path in the cmd_line. - std::wstring cmd_line = L"\""; - cmd_line += path; - cmd_line += L"\""; - int result2 = CreateProcessHelper(std::wstring(), cmd_line); - - // TEST 3: Try file name in the cmd_line. - int result3 = CreateProcessHelper(std::wstring(), argv[0]); - - // TEST 4: Try file name in the app_name and current directory sets correctly. - std::wstring system32 = MakeFullPathToSystem32(L""); - wchar_t current_directory[MAX_PATH + 1]; - int result4; - bool test_succeeded = false; - DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory); - if (0 != ret && ret < MAX_PATH) { - current_directory[ret] = L'\\'; - current_directory[ret+1] = L'\0'; - if (::SetCurrentDirectory(system32.c_str())) { - result4 = CreateProcessHelper(argv[0], std::wstring()); - if (::SetCurrentDirectory(current_directory)) { - test_succeeded = true; - } - } - } - if (!test_succeeded) - result4 = SBOX_TEST_FAILED; - - // TEST 5: Try with the path in the cmd_line and arguments. - cmd_line = L"\""; - cmd_line += path; - cmd_line += L"\" /INSERT"; - int result5 = CreateProcessHelper(std::wstring(), cmd_line); - - // TEST 6: Try with the file_name in the cmd_line and arguments. - cmd_line = argv[0]; - cmd_line += L" /INSERT"; - int result6 = CreateProcessHelper(std::wstring(), cmd_line); - - // TEST 7: Try with the path without the drive. - cmd_line = path.substr(path.find(L'\\')); - int result7 = CreateProcessHelper(std::wstring(), cmd_line); - - // Check if they all returned the same thing. - if ((result1 == result2) && (result2 == result3) && (result3 == result4) && - (result4 == result5) && (result5 == result6) && (result6 == result7)) - return result1; - - return SBOX_TEST_FAILED; -} - -// Creates a process and checks if it's possible to get a handle to it's token. -SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t **argv) { - if (argc != 1) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - if ((NULL == argv) || (NULL == argv[0])) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - std::wstring path = MakeFullPathToSystem32(argv[0]); - - base::win::ScopedProcessInformation pi; - STARTUPINFOW si = {sizeof(si)}; - - if (!::CreateProcessW(path.c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, - NULL, NULL, &si, pi.Receive())) { - return SBOX_TEST_FAILED; - } - - HANDLE token = NULL; - BOOL result = - ::OpenProcessToken(pi.process_handle(), TOKEN_IMPERSONATE, &token); - DWORD error = ::GetLastError(); - - base::win::ScopedHandle token_handle(token); - - if (!::TerminateProcess(pi.process_handle(), 0)) - return SBOX_TEST_FAILED; - - if (result && token) - return SBOX_TEST_SUCCEEDED; - - if (ERROR_ACCESS_DENIED == error) - return SBOX_TEST_DENIED; - - return SBOX_TEST_FAILED; -} - - -SBOX_TESTS_COMMAND int Process_OpenToken(int argc, wchar_t **argv) { - HANDLE token; - if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) { - if (ERROR_ACCESS_DENIED == ::GetLastError()) { - return SBOX_TEST_DENIED; - } - } else { - ::CloseHandle(token); - return SBOX_TEST_SUCCEEDED; - } - - return SBOX_TEST_FAILED; -} - -TEST(ProcessPolicyTest, TestAllAccess) { - // Check if the "all access" rule fails to be added when the token is too - // powerful. - TestRunner runner; - - // Check the failing case. - runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); - EXPECT_EQ(SBOX_ERROR_UNSUPPORTED, - runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS, - TargetPolicy::PROCESS_ALL_EXEC, - L"this is not important")); - - // Check the working case. - runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_INTERACTIVE); - - EXPECT_EQ(SBOX_ALL_OK, - runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS, - TargetPolicy::PROCESS_ALL_EXEC, - L"this is not important")); -} - -// This test is disabled. See bug 1305476. -TEST(ProcessPolicyTest, DISABLED_RunFindstrExe) { - TestRunner runner; - std::wstring exe_path = MakeFullPathToSystem32(L"findstr.exe"); - std::wstring system32 = MakeFullPathToSystem32(L""); - ASSERT_TRUE(!exe_path.empty()); - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, - TargetPolicy::PROCESS_MIN_EXEC, - exe_path.c_str())); - - // Need to add directory rules for the directories that we use in - // SetCurrentDirectory. - EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, - system32.c_str())); - - wchar_t current_directory[MAX_PATH]; - DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory); - ASSERT_TRUE(0 != ret && ret < MAX_PATH); - - wcscat_s(current_directory, MAX_PATH, L"\\"); - EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, - current_directory)); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_RunApp findstr.exe")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp calc.exe")); -} - -TEST(ProcessPolicyTest, OpenToken) { - TestRunner runner; - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_OpenToken")); -} - -TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) { - TestRunner runner; - std::wstring exe_path = MakeFullPathToSystem32(L"findstr.exe"); - ASSERT_TRUE(!exe_path.empty()); - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, - TargetPolicy::PROCESS_MIN_EXEC, - exe_path.c_str())); - - EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); -} - -TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) { - TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE); - std::wstring exe_path = MakeFullPathToSystem32(L"findstr.exe"); - ASSERT_TRUE(!exe_path.empty()); - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, - TargetPolicy::PROCESS_ALL_EXEC, - exe_path.c_str())); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); -} - -} // namespace sandbox diff --git a/sandbox/src/process_thread_dispatcher.cc b/sandbox/src/process_thread_dispatcher.cc deleted file mode 100644 index cc137a5..0000000 --- a/sandbox/src/process_thread_dispatcher.cc +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/process_thread_dispatcher.h" - -#include "base/basictypes.h" -#include "base/logging.h" -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/interception.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_broker.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/process_thread_interception.h" -#include "sandbox/src/process_thread_policy.h" -#include "sandbox/src/sandbox.h" - -namespace { - -// Extracts the application name from a command line. -// -// The application name is the first element of the command line. If -// there is no quotes, the first element is delimited by the first space. -// If there are quotes, the first element is delimited by the quotes. -// -// The create process call is smarter than us. It tries really hard to launch -// the process even if the command line is wrong. For example: -// "c:\program files\test param" will first try to launch c:\program.exe then -// c:\program files\test.exe. We don't do that, we stop after at the first -// space when there is no quotes. -std::wstring GetPathFromCmdLine(const std::wstring &cmd_line) { - std::wstring exe_name; - // Check if it starts with '"'. - if (cmd_line[0] == L'\"') { - // Find the position of the second '"', this terminates the path. - std::wstring::size_type pos = cmd_line.find(L'\"', 1); - if (std::wstring::npos == pos) - return cmd_line; - exe_name = cmd_line.substr(1, pos - 1); - } else { - // There is no '"', that means that the appname is terminated at the - // first space. - std::wstring::size_type pos = cmd_line.find(L' '); - if (std::wstring::npos == pos) { - // There is no space, the cmd_line contains only the app_name - exe_name = cmd_line; - } else { - exe_name = cmd_line.substr(0, pos); - } - } - - return exe_name; -} - -// Returns true is the path in parameter is relative. False if it's -// absolute. -bool IsPathRelative(const std::wstring &path) { - // A path is Relative if it's not a UNC path beginnning with \\ or a - // path beginning with a drive. (i.e. X:\) - if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1) - return false; - return true; -} - -// Converts a relative path to an absolute path. -bool ConvertToAbsolutePath(const std::wstring child_current_directory, - bool use_env_path, std::wstring *path) { - wchar_t file_buffer[MAX_PATH]; - wchar_t *file_part = NULL; - - // Here we should start by looking at the path where the child application was - // started. We don't have this information yet. - DWORD result = 0; - if (use_env_path) { - // Try with the complete path - result = ::SearchPath(NULL, path->c_str(), NULL, MAX_PATH, file_buffer, - &file_part); - } - - if (0 == result) { - // Try with the current directory of the child - result = ::SearchPath(child_current_directory.c_str(), path->c_str(), NULL, - MAX_PATH, file_buffer, &file_part); - } - - if (0 == result || result >= MAX_PATH) - return false; - - *path = file_buffer; - return true; -} - -} // namespace -namespace sandbox { - -ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base) - : policy_base_(policy_base) { - static const IPCCall open_thread = { - {IPC_NTOPENTHREAD_TAG, ULONG_TYPE, ULONG_TYPE}, - reinterpret_cast( - &ThreadProcessDispatcher::NtOpenThread) - }; - - static const IPCCall open_process = { - {IPC_NTOPENPROCESS_TAG, ULONG_TYPE, ULONG_TYPE}, - reinterpret_cast( - &ThreadProcessDispatcher::NtOpenProcess) - }; - - static const IPCCall process_token = { - {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, ULONG_TYPE}, - reinterpret_cast( - &ThreadProcessDispatcher::NtOpenProcessToken) - }; - - static const IPCCall process_tokenex = { - {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE}, - reinterpret_cast( - &ThreadProcessDispatcher::NtOpenProcessTokenEx) - }; - - static const IPCCall create_params = { - {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE}, - reinterpret_cast( - &ThreadProcessDispatcher::CreateProcessW) - }; - - ipc_calls_.push_back(open_thread); - ipc_calls_.push_back(open_process); - ipc_calls_.push_back(process_token); - ipc_calls_.push_back(process_tokenex); - ipc_calls_.push_back(create_params); -} - -bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager, - int service) { - switch (service) { - case IPC_NTOPENTHREAD_TAG: - case IPC_NTOPENPROCESS_TAG: - case IPC_NTOPENPROCESSTOKEN_TAG: - case IPC_NTOPENPROCESSTOKENEX_TAG: - // There is no explicit policy for these services. - NOTREACHED(); - return false; - - case IPC_CREATEPROCESSW_TAG: - return INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessW, - CREATE_PROCESSW_ID, 44) && - INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA, - CREATE_PROCESSA_ID, 44); - - default: - return false; - } -} - -bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, DWORD desired_access, - DWORD thread_id) { - HANDLE handle; - NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info, - desired_access, thread_id, - &handle); - ipc->return_info.nt_status = ret; - ipc->return_info.handle = handle; - return true; -} - -bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, DWORD desired_access, - DWORD process_id) { - HANDLE handle; - NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info, - desired_access, process_id, - &handle); - ipc->return_info.nt_status = ret; - ipc->return_info.handle = handle; - return true; -} - -bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, HANDLE process, - DWORD desired_access) { - HANDLE handle; - NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info, - process, desired_access, - &handle); - ipc->return_info.nt_status = ret; - ipc->return_info.handle = handle; - return true; -} - -bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, - DWORD desired_access, - DWORD attributes) { - HANDLE handle; - NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info, - process, - desired_access, - attributes, &handle); - ipc->return_info.nt_status = ret; - ipc->return_info.handle = handle; - return true; -} - -bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, std::wstring* name, - std::wstring* cmd_line, - std::wstring* cur_dir, - CountedBuffer* info) { - if (sizeof(PROCESS_INFORMATION) != info->Size()) - return false; - - // Check if there is an application name. - std::wstring exe_name; - if (!name->empty()) - exe_name = *name; - else - exe_name = GetPathFromCmdLine(*cmd_line); - - if (IsPathRelative(exe_name)) { - if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) { - // Cannot find the path. Maybe the file does not exist. - ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND; - return true; - } - } - - const wchar_t* const_exe_name = exe_name.c_str(); - CountedParameterSet params; - params[NameBased::NAME] = ParamPickerMake(const_exe_name); - - EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG, - params.GetBase()); - - PROCESS_INFORMATION* proc_info = - reinterpret_cast(info->Buffer()); - // Here we force the app_name to be the one we used for the policy lookup. - // If our logic was wrong, at least we wont allow create a random process. - DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info, - exe_name, *cmd_line, - proc_info); - - ipc->return_info.win32_result = ret; - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/process_thread_dispatcher.h b/sandbox/src/process_thread_dispatcher.h deleted file mode 100644 index 45fad03..0000000 --- a/sandbox/src/process_thread_dispatcher.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_ -#define SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_ - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sandbox_policy_base.h" - -namespace sandbox { - -// This class handles process and thread-related IPC calls. -class ThreadProcessDispatcher : public Dispatcher { - public: - explicit ThreadProcessDispatcher(PolicyBase* policy_base); - ~ThreadProcessDispatcher() {} - - // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); - - private: - // Processes IPC requests coming from calls to NtOpenThread() in the target. - bool NtOpenThread(IPCInfo* ipc, DWORD desired_access, DWORD thread_id); - - // Processes IPC requests coming from calls to NtOpenProcess() in the target. - bool NtOpenProcess(IPCInfo* ipc, DWORD desired_access, DWORD process_id); - - // Processes IPC requests from calls to NtOpenProcessToken() in the target. - bool NtOpenProcessToken(IPCInfo* ipc, HANDLE process, DWORD desired_access); - - // Processes IPC requests from calls to NtOpenProcessTokenEx() in the target. - bool NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, DWORD desired_access, - DWORD attributes); - - // Processes IPC requests coming from calls to CreateProcessW() in the target. - bool CreateProcessW(IPCInfo* ipc, std::wstring* name, std::wstring* cmd_line, - std::wstring* cur_dir, CountedBuffer* info); - - PolicyBase* policy_base_; - DISALLOW_COPY_AND_ASSIGN(ThreadProcessDispatcher); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_ diff --git a/sandbox/src/process_thread_interception.cc b/sandbox/src/process_thread_interception.cc deleted file mode 100644 index e847908..0000000 --- a/sandbox/src/process_thread_interception.cc +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/process_thread_interception.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/policy_target.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/target_services.h" - -namespace sandbox { - -SANDBOX_INTERCEPT NtExports g_nt; - -// Hooks NtOpenThread and proxy the call to the broker if it's trying to -// open a thread in the same process. -NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, - PHANDLE thread, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, - PCLIENT_ID client_id) { - NTSTATUS status = orig_OpenThread(thread, desired_access, object_attributes, - client_id); - if (NT_SUCCESS(status)) - return status; - - do { - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - break; - if (!client_id) - break; - - uint32 thread_id = 0; - bool should_break = false; - __try { - // We support only the calls for the current process - if (NULL != client_id->UniqueProcess) - should_break = true; - - // Object attributes should be NULL or empty. - if (!should_break && NULL != object_attributes) { - if (0 != object_attributes->Attributes || - NULL != object_attributes->ObjectName || - NULL != object_attributes->RootDirectory || - NULL != object_attributes->SecurityDescriptor || - NULL != object_attributes->SecurityQualityOfService) { - should_break = true; - } - } - - thread_id = static_cast( - reinterpret_cast(client_id->UniqueThread)); - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - - if (should_break) - break; - - if (!ValidParameter(thread, sizeof(HANDLE), WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_NTOPENTHREAD_TAG, desired_access, - thread_id, &answer); - if (SBOX_ALL_OK != code) - break; - - if (!NT_SUCCESS(answer.nt_status)) - // The nt_status here is most likely STATUS_INVALID_CID because - // in the broker we set the process id in the CID (client ID) param - // to be the current process. If you try to open a thread from another - // process you will get this INVALID_CID error. On the other hand, if you - // try to open a thread in your own process, it should return success. - // We don't want to return STATUS_INVALID_CID here, so we return the - // return of the original open thread status, which is most likely - // STATUS_ACCESS_DENIED. - break; - - __try { - // Write the output parameters. - *thread = answer.handle; - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - - return answer.nt_status; - } while (false); - - return status; -} - -// Hooks NtOpenProcess and proxy the call to the broker if it's trying to -// open the current process. -NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess, - PHANDLE process, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, - PCLIENT_ID client_id) { - NTSTATUS status = orig_OpenProcess(process, desired_access, object_attributes, - client_id); - if (NT_SUCCESS(status)) - return status; - - do { - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - break; - if (!client_id) - break; - - uint32 process_id = 0; - bool should_break = false; - __try { - // Object attributes should be NULL or empty. - if (!should_break && NULL != object_attributes) { - if (0 != object_attributes->Attributes || - NULL != object_attributes->ObjectName || - NULL != object_attributes->RootDirectory || - NULL != object_attributes->SecurityDescriptor || - NULL != object_attributes->SecurityQualityOfService) { - should_break = true; - } - } - - process_id = static_cast( - reinterpret_cast(client_id->UniqueProcess)); - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - - if (should_break) - break; - - if (!ValidParameter(process, sizeof(HANDLE), WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESS_TAG, desired_access, - process_id, &answer); - if (SBOX_ALL_OK != code) - break; - - if (!NT_SUCCESS(answer.nt_status)) - return answer.nt_status; - - __try { - // Write the output parameters. - *process = answer.handle; - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - - return answer.nt_status; - } while (false); - - return status; -} - - -NTSTATUS WINAPI TargetNtOpenProcessToken( - NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process, - ACCESS_MASK desired_access, PHANDLE token) { - NTSTATUS status = orig_OpenProcessToken(process, desired_access, token); - if (NT_SUCCESS(status)) - return status; - - do { - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - break; - - if (CURRENT_PROCESS != process) - break; - - if (!ValidParameter(token, sizeof(HANDLE), WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKEN_TAG, process, - desired_access, &answer); - if (SBOX_ALL_OK != code) - break; - - if (!NT_SUCCESS(answer.nt_status)) - return answer.nt_status; - - __try { - // Write the output parameters. - *token = answer.handle; - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - - return answer.nt_status; - } while (false); - - return status; -} - -NTSTATUS WINAPI TargetNtOpenProcessTokenEx( - NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process, - ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token) { - NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access, - handle_attributes, token); - if (NT_SUCCESS(status)) - return status; - - do { - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - break; - - if (CURRENT_PROCESS != process) - break; - - if (!ValidParameter(token, sizeof(HANDLE), WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKENEX_TAG, process, - desired_access, handle_attributes, &answer); - if (SBOX_ALL_OK != code) - break; - - if (!NT_SUCCESS(answer.nt_status)) - return answer.nt_status; - - __try { - // Write the output parameters. - *token = answer.handle; - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - - return answer.nt_status; - } while (false); - - return status; -} - -BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW, - LPCWSTR application_name, LPWSTR command_line, - LPSECURITY_ATTRIBUTES process_attributes, - LPSECURITY_ATTRIBUTES thread_attributes, - BOOL inherit_handles, DWORD flags, - LPVOID environment, LPCWSTR current_directory, - LPSTARTUPINFOW startup_info, - LPPROCESS_INFORMATION process_information) { - if (orig_CreateProcessW(application_name, command_line, process_attributes, - thread_attributes, inherit_handles, flags, - environment, current_directory, startup_info, - process_information)) { - return TRUE; - } - DWORD original_error = ::GetLastError(); - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return FALSE; - - do { - if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), - WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - const wchar_t* cur_dir = NULL; - - wchar_t current_directory[MAX_PATH]; - DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); - if (0 != result && result < MAX_PATH) - cur_dir = current_directory; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - - InOutCountedBuffer proc_info(process_information, - sizeof(PROCESS_INFORMATION)); - - ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name, - command_line, cur_dir, proc_info, &answer); - if (SBOX_ALL_OK != code) - break; - - ::SetLastError(answer.win32_result); - if (ERROR_SUCCESS != answer.win32_result) - return FALSE; - - return TRUE; - } while (false); - - ::SetLastError(original_error); - return FALSE; -} - -BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA, - LPCSTR application_name, LPSTR command_line, - LPSECURITY_ATTRIBUTES process_attributes, - LPSECURITY_ATTRIBUTES thread_attributes, - BOOL inherit_handles, DWORD flags, - LPVOID environment, LPCSTR current_directory, - LPSTARTUPINFOA startup_info, - LPPROCESS_INFORMATION process_information) { - if (orig_CreateProcessA(application_name, command_line, process_attributes, - thread_attributes, inherit_handles, flags, - environment, current_directory, startup_info, - process_information)) { - return TRUE; - } - DWORD original_error = ::GetLastError(); - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return FALSE; - - do { - if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), - WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - // Convert the input params to unicode. - UNICODE_STRING *cmd_unicode = NULL; - UNICODE_STRING *app_unicode = NULL; - if (command_line) { - cmd_unicode = AnsiToUnicode(command_line); - if (!cmd_unicode) - break; - } - - if (application_name) { - app_unicode = AnsiToUnicode(application_name); - if (!app_unicode) { - operator delete(cmd_unicode, NT_ALLOC); - break; - } - } - - const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL; - const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL; - const wchar_t* cur_dir = NULL; - - wchar_t current_directory[MAX_PATH]; - DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); - if (0 != result && result < MAX_PATH) - cur_dir = current_directory; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - - InOutCountedBuffer proc_info(process_information, - sizeof(PROCESS_INFORMATION)); - - ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name, - cmd_line, cur_dir, proc_info, &answer); - - operator delete(cmd_unicode, NT_ALLOC); - operator delete(app_unicode, NT_ALLOC); - - if (SBOX_ALL_OK != code) - break; - - ::SetLastError(answer.win32_result); - if (ERROR_SUCCESS != answer.win32_result) - return FALSE; - - return TRUE; - } while (false); - - ::SetLastError(original_error); - return FALSE; -} - -// Creates a thread without registering with CSRSS. This is required if we -// closed the CSRSS ALPC port after lockdown. -HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread, - LPSECURITY_ATTRIBUTES thread_attributes, - SIZE_T stack_size, - LPTHREAD_START_ROUTINE start_address, - PVOID parameter, - DWORD creation_flags, - LPDWORD thread_id) { -// Try the normal CreateThread; switch to RtlCreateUserThread if needed. - static bool use_create_thread = true; - HANDLE thread; - if (use_create_thread) { - thread = orig_CreateThread(thread_attributes, stack_size, start_address, - parameter, creation_flags, thread_id); - if (thread) - return thread; - } - - PSECURITY_DESCRIPTOR sd = - thread_attributes ? thread_attributes->lpSecurityDescriptor : NULL; - CLIENT_ID client_id; - - NTSTATUS result = g_nt.RtlCreateUserThread(NtCurrentProcess, sd, - creation_flags & CREATE_SUSPENDED, - 0, stack_size, 0, start_address, - parameter, &thread, &client_id); - if (!NT_SUCCESS(result)) - return 0; - - // CSRSS is closed if we got here, so use RtlCreateUserThread from here on. - use_create_thread = false; - if (thread_id) - *thread_id = HandleToUlong(client_id.UniqueThread); - return thread; -} - -// Cache the default LCID to avoid pinging CSRSS after lockdown. -// TODO(jschuh): This approach will miss a default locale changes after -// lockdown. In the future we may want to have the broker check instead. -LCID WINAPI TargetGetUserDefaultLCID( - GetUserDefaultLCIDFunction orig_GetUserDefaultLCID) { - static LCID default_lcid = orig_GetUserDefaultLCID(); - return default_lcid; -} - -} // namespace sandbox diff --git a/sandbox/src/process_thread_interception.h b/sandbox/src/process_thread_interception.h deleted file mode 100644 index 37c2c14..0000000 --- a/sandbox/src/process_thread_interception.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -#ifndef SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__ -#define SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__ - -namespace sandbox { - -extern "C" { - -typedef BOOL (WINAPI *CreateProcessWFunction)( - LPCWSTR lpApplicationName, - LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation); - -typedef BOOL (WINAPI *CreateProcessAFunction)( - LPCSTR lpApplicationName, - LPSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCSTR lpCurrentDirectory, - LPSTARTUPINFOA lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation); - -typedef HANDLE (WINAPI *CreateThreadFunction)( - LPSECURITY_ATTRIBUTES lpThreadAttributes, - SIZE_T dwStackSize, - LPTHREAD_START_ROUTINE lpStartAddress, - PVOID lpParameter, - DWORD dwCreationFlags, - LPDWORD lpThreadId); - -typedef LCID (WINAPI *GetUserDefaultLCIDFunction)(); - -// Interception of NtOpenThread on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThread( - NtOpenThreadFunction orig_OpenThread, PHANDLE thread, - ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes, - PCLIENT_ID client_id); - -// Interception of NtOpenProcess on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcess( - NtOpenProcessFunction orig_OpenProcess, PHANDLE process, - ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes, - PCLIENT_ID client_id); - -// Interception of NtOpenProcessToken on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessToken( - NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process, - ACCESS_MASK desired_access, PHANDLE token); - -// Interception of NtOpenProcessTokenEx on the child process. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessTokenEx( - NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process, - ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token); - -// Interception of CreateProcessW and A in kernel32.dll. -SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessW( - CreateProcessWFunction orig_CreateProcessW, LPCWSTR application_name, - LPWSTR command_line, LPSECURITY_ATTRIBUTES process_attributes, - LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, - LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info, - LPPROCESS_INFORMATION process_information); - -SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA( - CreateProcessAFunction orig_CreateProcessA, LPCSTR application_name, - LPSTR command_line, LPSECURITY_ATTRIBUTES process_attributes, - LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, - LPVOID environment, LPCSTR current_directory, LPSTARTUPINFOA startup_info, - LPPROCESS_INFORMATION process_information); - -// Interception of CreateThread in kernel32.dll. -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateThread( - CreateThreadFunction orig_CreateThread, - LPSECURITY_ATTRIBUTES thread_attributes, SIZE_T stack_size, - LPTHREAD_START_ROUTINE start_address, PVOID parameter, - DWORD creation_flags, LPDWORD thread_id); - -// Interception of GetUserDefaultLCID in kernel32.dll. -SANDBOX_INTERCEPT LCID WINAPI TargetGetUserDefaultLCID( - GetUserDefaultLCIDFunction orig_GetUserDefaultLCID); - -} // extern "C" - -} // namespace sandbox - -#endif // SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__ diff --git a/sandbox/src/process_thread_policy.cc b/sandbox/src/process_thread_policy.cc deleted file mode 100644 index ca00916..0000000 --- a/sandbox/src/process_thread_policy.cc +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/process_thread_policy.h" - -#include - -#include "base/memory/scoped_ptr.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/policy_engine_opcodes.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/win_utils.h" - -namespace { - -// These are the only safe rights that can be given to a sandboxed -// process for the process created by the broker. All others are potential -// vectors of privilege elevation. -const DWORD kProcessRights = SYNCHRONIZE | - PROCESS_QUERY_INFORMATION | - PROCESS_QUERY_LIMITED_INFORMATION | - PROCESS_TERMINATE | - PROCESS_SUSPEND_RESUME; - -const DWORD kThreadRights = SYNCHRONIZE | - THREAD_TERMINATE | - THREAD_SUSPEND_RESUME | - THREAD_QUERY_INFORMATION | - THREAD_QUERY_LIMITED_INFORMATION | - THREAD_SET_LIMITED_INFORMATION; - -// Creates a child process and duplicates the handles to 'target_process'. The -// remaining parameters are the same as CreateProcess(). -BOOL CreateProcessExWHelper(HANDLE target_process, BOOL give_full_access, - LPCWSTR lpApplicationName, LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, DWORD dwCreationFlags, - LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation) { - if (!::CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, - lpThreadAttributes, bInheritHandles, dwCreationFlags, - lpEnvironment, lpCurrentDirectory, lpStartupInfo, - lpProcessInformation)) { - return FALSE; - } - - DWORD process_access = kProcessRights; - DWORD thread_access = kThreadRights; - if (give_full_access) { - process_access = PROCESS_ALL_ACCESS; - thread_access = THREAD_ALL_ACCESS; - } - if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hProcess, - target_process, &lpProcessInformation->hProcess, - process_access, FALSE, DUPLICATE_CLOSE_SOURCE)) { - ::CloseHandle(lpProcessInformation->hThread); - return FALSE; - } - if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hThread, - target_process, &lpProcessInformation->hThread, - thread_access, FALSE, DUPLICATE_CLOSE_SOURCE)) { - return FALSE; - } - return TRUE; -} - -} - -namespace sandbox { - -bool ProcessPolicy::GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy) { - scoped_ptr process; - switch (semantics) { - case TargetPolicy::PROCESS_MIN_EXEC: { - process.reset(new PolicyRule(GIVE_READONLY)); - break; - }; - case TargetPolicy::PROCESS_ALL_EXEC: { - process.reset(new PolicyRule(GIVE_ALLACCESS)); - break; - }; - default: { - return false; - }; - } - - if (!process->AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) { - return false; - } - if (!policy->AddRule(IPC_CREATEPROCESSW_TAG, process.get())) { - return false; - } - return true; -} - -NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info, - uint32 desired_access, - uint32 thread_id, - HANDLE* handle) { - *handle = NULL; - - NtOpenThreadFunction NtOpenThread = NULL; - ResolveNTFunctionPtr("NtOpenThread", &NtOpenThread); - - OBJECT_ATTRIBUTES attributes = {0}; - attributes.Length = sizeof(attributes); - CLIENT_ID client_id = {0}; - client_id.UniqueProcess = reinterpret_cast( - static_cast(client_info.process_id)); - client_id.UniqueThread = - reinterpret_cast(static_cast(thread_id)); - - HANDLE local_handle; - NTSTATUS status = NtOpenThread(&local_handle, desired_access, &attributes, - &client_id); - if (NT_SUCCESS(status)) { - if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - client_info.process, handle, 0, FALSE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(local_handle); - return STATUS_ACCESS_DENIED; - } - } - - return status; -} - -NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info, - uint32 desired_access, - uint32 process_id, - HANDLE* handle) { - *handle = NULL; - - NtOpenProcessFunction NtOpenProcess = NULL; - ResolveNTFunctionPtr("NtOpenProcess", &NtOpenProcess); - - if (client_info.process_id != process_id) - return STATUS_ACCESS_DENIED; - - OBJECT_ATTRIBUTES attributes = {0}; - attributes.Length = sizeof(attributes); - CLIENT_ID client_id = {0}; - client_id.UniqueProcess = reinterpret_cast( - static_cast(client_info.process_id)); - HANDLE local_handle; - NTSTATUS status = NtOpenProcess(&local_handle, desired_access, &attributes, - &client_id); - if (NT_SUCCESS(status)) { - if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - client_info.process, handle, 0, FALSE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(local_handle); - return STATUS_ACCESS_DENIED; - } - } - - return status; -} - -NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info, - HANDLE process, - uint32 desired_access, - HANDLE* handle) { - *handle = NULL; - NtOpenProcessTokenFunction NtOpenProcessToken = NULL; - ResolveNTFunctionPtr("NtOpenProcessToken", &NtOpenProcessToken); - - if (CURRENT_PROCESS != process) - return STATUS_ACCESS_DENIED; - - HANDLE local_handle; - NTSTATUS status = NtOpenProcessToken(client_info.process, desired_access, - &local_handle); - if (NT_SUCCESS(status)) { - if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - client_info.process, handle, 0, FALSE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(local_handle); - return STATUS_ACCESS_DENIED; - } - } - return status; -} - -NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info, - HANDLE process, - uint32 desired_access, - uint32 attributes, - HANDLE* handle) { - *handle = NULL; - NtOpenProcessTokenExFunction NtOpenProcessTokenEx = NULL; - ResolveNTFunctionPtr("NtOpenProcessTokenEx", &NtOpenProcessTokenEx); - - if (CURRENT_PROCESS != process) - return STATUS_ACCESS_DENIED; - - HANDLE local_handle; - NTSTATUS status = NtOpenProcessTokenEx(client_info.process, desired_access, - attributes, &local_handle); - if (NT_SUCCESS(status)) { - if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - client_info.process, handle, 0, FALSE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(local_handle); - return STATUS_ACCESS_DENIED; - } - } - return status; -} - -DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &app_name, - const std::wstring &command_line, - PROCESS_INFORMATION* process_info) { - // The only action supported is ASK_BROKER which means create the process. - if (GIVE_ALLACCESS != eval_result && GIVE_READONLY != eval_result) { - return ERROR_ACCESS_DENIED; - } - - STARTUPINFO startup_info = {0}; - startup_info.cb = sizeof(startup_info); - scoped_ptr_malloc cmd_line(_wcsdup(command_line.c_str())); - - BOOL should_give_full_access = (GIVE_ALLACCESS == eval_result); - if (!CreateProcessExWHelper(client_info.process, should_give_full_access, - app_name.c_str(), cmd_line.get(), NULL, NULL, - FALSE, 0, NULL, NULL, &startup_info, - process_info)) { - return ERROR_ACCESS_DENIED; - } - return ERROR_SUCCESS; -} - -} // namespace sandbox diff --git a/sandbox/src/process_thread_policy.h b/sandbox/src/process_thread_policy.h deleted file mode 100644 index 78323cc..0000000 --- a/sandbox/src/process_thread_policy.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_PROCESS_THREAD_POLICY_H_ -#define SANDBOX_SRC_PROCESS_THREAD_POLICY_H_ - -#include - -#include "sandbox/src/policy_low_level.h" - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sandbox_policy.h" - -namespace sandbox { - -enum EvalResult; - -// This class centralizes most of the knowledge related to process execution. -class ProcessPolicy { - public: - // Creates the required low-level policy rules to evaluate a high-level. - // policy rule for process creation - // 'name' is the executable to be spawn. - // 'semantics' is the desired semantics. - // 'policy' is the policy generator to which the rules are going to be added. - static bool GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy); - - // Opens a thread from the child process and returns the handle. - // client_info contains the information about the child process, - // desired_access is the access requested by the child and thread_id - // is the thread_id to be opened. - // The function returns the return value of NtOpenThread. - static NTSTATUS OpenThreadAction(const ClientInfo& client_info, - uint32 desired_access, - uint32 thread_id, - HANDLE* handle); - - // Opens the process id passed in and returns the duplicated handle to - // the child. We only allow the child processes to open themselves. Any other - // pid open is denied. - static NTSTATUS OpenProcessAction(const ClientInfo& client_info, - uint32 desired_access, - uint32 process_id, - HANDLE* handle); - - // Opens the token associated with the process and returns the duplicated - // handle to the child. We only allow the child processes to open his own - // token (using ::GetCurrentProcess()). - static NTSTATUS OpenProcessTokenAction(const ClientInfo& client_info, - HANDLE process, - uint32 desired_access, - HANDLE* handle); - - // Opens the token associated with the process and returns the duplicated - // handle to the child. We only allow the child processes to open his own - // token (using ::GetCurrentProcess()). - static NTSTATUS OpenProcessTokenExAction(const ClientInfo& client_info, - HANDLE process, - uint32 desired_access, - uint32 attributes, - HANDLE* handle); - - // Processes a 'CreateProcessW()' request from the target. - // 'client_info' : the target process that is making the request. - // 'eval_result' : The desired policy action to accomplish. - // 'app_name' : The full path of the process to be created. - // 'command_line' : The command line passed to the created process. - static DWORD CreateProcessWAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &app_name, - const std::wstring &command_line, - PROCESS_INFORMATION* process_info); -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_PROCESS_THREAD_POLICY_H_ diff --git a/sandbox/src/registry_dispatcher.cc b/sandbox/src/registry_dispatcher.cc deleted file mode 100644 index f86c76a..0000000 --- a/sandbox/src/registry_dispatcher.cc +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/registry_dispatcher.h" - -#include "base/win/scoped_handle.h" -#include "base/win/windows_version.h" -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/interception.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/policy_broker.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/registry_interception.h" -#include "sandbox/src/registry_policy.h" - -namespace { - -// Builds a path using the root directory and the name. -bool GetCompletePath(HANDLE root, const std::wstring& name, - std::wstring* complete_name) { - if (root) { - if (!sandbox::GetPathFromHandle(root, complete_name)) - return false; - - *complete_name += L"\\"; - *complete_name += name; - } else { - *complete_name = name; - } - - return true; -} - -} - -namespace sandbox { - -RegistryDispatcher::RegistryDispatcher(PolicyBase* policy_base) - : policy_base_(policy_base) { - static const IPCCall create_params = { - {IPC_NTCREATEKEY_TAG, WCHAR_TYPE, ULONG_TYPE, VOIDPTR_TYPE, ULONG_TYPE, - ULONG_TYPE, ULONG_TYPE}, - reinterpret_cast(&RegistryDispatcher::NtCreateKey) - }; - - static const IPCCall open_params = { - {IPC_NTOPENKEY_TAG, WCHAR_TYPE, ULONG_TYPE, VOIDPTR_TYPE, ULONG_TYPE}, - reinterpret_cast(&RegistryDispatcher::NtOpenKey) - }; - - ipc_calls_.push_back(create_params); - ipc_calls_.push_back(open_params); -} - -bool RegistryDispatcher::SetupService(InterceptionManager* manager, - int service) { - if (IPC_NTCREATEKEY_TAG == service) - return INTERCEPT_NT(manager, NtCreateKey, CREATE_KEY_ID, 32); - - if (IPC_NTOPENKEY_TAG == service) { - bool result = INTERCEPT_NT(manager, NtOpenKey, OPEN_KEY_ID, 16); - if (base::win::GetVersion() >= base::win::VERSION_WIN7) - result &= INTERCEPT_NT(manager, NtOpenKeyEx, OPEN_KEY_EX_ID, 20); - return result; - } - - return false; -} - -bool RegistryDispatcher::NtCreateKey( - IPCInfo* ipc, std::wstring* name, DWORD attributes, HANDLE root, - DWORD desired_access, DWORD title_index, DWORD create_options) { - base::win::ScopedHandle root_handle; - std::wstring real_path = *name; - - // If there is a root directory, we need to duplicate the handle to make - // it valid in this process. - if (root) { - if (!::DuplicateHandle(ipc->client_info->process, root, - ::GetCurrentProcess(), &root, 0, FALSE, - DUPLICATE_SAME_ACCESS)) - return false; - - root_handle.Set(root); - } - - if (!GetCompletePath(root, *name, &real_path)) - return false; - - const wchar_t* regname = real_path.c_str(); - CountedParameterSet params; - params[OpenKey::NAME] = ParamPickerMake(regname); - params[OpenKey::ACCESS] = ParamPickerMake(desired_access); - - EvalResult result = policy_base_->EvalPolicy(IPC_NTCREATEKEY_TAG, - params.GetBase()); - - HANDLE handle; - NTSTATUS nt_status; - ULONG disposition = 0; - if (!RegistryPolicy::CreateKeyAction(result, *ipc->client_info, *name, - attributes, root, desired_access, - title_index, create_options, &handle, - &nt_status, &disposition)) { - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - // Return operation status on the IPC. - ipc->return_info.extended[0].unsigned_int = disposition; - ipc->return_info.nt_status = nt_status; - ipc->return_info.handle = handle; - return true; -} - -bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc, std::wstring* name, - DWORD attributes, HANDLE root, - DWORD desired_access) { - base::win::ScopedHandle root_handle; - std::wstring real_path = *name; - - // If there is a root directory, we need to duplicate the handle to make - // it valid in this process. - if (root) { - if (!::DuplicateHandle(ipc->client_info->process, root, - ::GetCurrentProcess(), &root, 0, FALSE, - DUPLICATE_SAME_ACCESS)) - return false; - root_handle.Set(root); - } - - if (!GetCompletePath(root, *name, &real_path)) - return false; - - const wchar_t* regname = real_path.c_str(); - CountedParameterSet params; - params[OpenKey::NAME] = ParamPickerMake(regname); - params[OpenKey::ACCESS] = ParamPickerMake(desired_access); - - EvalResult result = policy_base_->EvalPolicy(IPC_NTOPENKEY_TAG, - params.GetBase()); - HANDLE handle; - NTSTATUS nt_status; - if (!RegistryPolicy::OpenKeyAction(result, *ipc->client_info, *name, - attributes, root, desired_access, &handle, - &nt_status)) { - ipc->return_info.nt_status = STATUS_ACCESS_DENIED; - return true; - } - - // Return operation status on the IPC. - ipc->return_info.nt_status = nt_status; - ipc->return_info.handle = handle; - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/registry_dispatcher.h b/sandbox/src/registry_dispatcher.h deleted file mode 100644 index 6d1390d..0000000 --- a/sandbox/src/registry_dispatcher.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_REGISTRY_DISPATCHER_H_ -#define SANDBOX_SRC_REGISTRY_DISPATCHER_H_ - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sandbox_policy_base.h" - -namespace sandbox { - -// This class handles registry-related IPC calls. -class RegistryDispatcher : public Dispatcher { - public: - explicit RegistryDispatcher(PolicyBase* policy_base); - ~RegistryDispatcher() {} - - // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); - - private: - // Processes IPC requests coming from calls to NtCreateKey in the target. - bool NtCreateKey(IPCInfo* ipc, std::wstring* name, DWORD attributes, - HANDLE root, DWORD desired_access, - DWORD title_index, DWORD create_options); - - // Processes IPC requests coming from calls to NtOpenKey in the target. - bool NtOpenKey(IPCInfo* ipc, std::wstring* name, DWORD attributes, - HANDLE root, DWORD desired_access); - - PolicyBase* policy_base_; - DISALLOW_COPY_AND_ASSIGN(RegistryDispatcher); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_REGISTRY_DISPATCHER_H_ diff --git a/sandbox/src/registry_interception.cc b/sandbox/src/registry_interception.cc deleted file mode 100644 index 3cabf47..0000000 --- a/sandbox/src/registry_interception.cc +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/registry_interception.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/target_services.h" - -namespace sandbox { - -NTSTATUS WINAPI TargetNtCreateKey(NtCreateKeyFunction orig_CreateKey, - PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, - ULONG title_index, PUNICODE_STRING class_name, - ULONG create_options, PULONG disposition) { - // Check if the process can create it first. - NTSTATUS status = orig_CreateKey(key, desired_access, object_attributes, - title_index, class_name, create_options, - disposition); - if (NT_SUCCESS(status)) - return status; - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return status; - - do { - if (!ValidParameter(key, sizeof(HANDLE), WRITE)) - break; - - if (disposition && !ValidParameter(disposition, sizeof(ULONG), WRITE)) - break; - - // At this point we don't support class_name. - if (class_name && class_name->Buffer && class_name->Length) - break; - - // We don't support creating link keys, volatile keys and backup/restore. - if (create_options) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - wchar_t* name; - uint32 attributes = 0; - HANDLE root_directory = 0; - NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, - &root_directory); - if (!NT_SUCCESS(ret) || NULL == name) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - - ResultCode code = CrossCall(ipc, IPC_NTCREATEKEY_TAG, name, attributes, - root_directory, desired_access, title_index, - create_options, &answer); - - operator delete(name, NT_ALLOC); - - if (SBOX_ALL_OK != code) - break; - - if (!NT_SUCCESS(answer.nt_status)) - // TODO(nsylvain): We should return answer.nt_status here instead - // of status. We can do this only after we checked the policy. - // otherwise we will returns ACCESS_DENIED for all paths - // that are not specified by a policy, even though your token allows - // access to that path, and the original call had a more meaningful - // error. Bug 4369 - break; - - __try { - *key = answer.handle; - - if (disposition) - *disposition = answer.extended[0].unsigned_int; - - status = answer.nt_status; - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - } while (false); - - return status; -} - -NTSTATUS WINAPI CommonNtOpenKey(NTSTATUS status, PHANDLE key, - ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes) { - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return status; - - do { - if (!ValidParameter(key, sizeof(HANDLE), WRITE)) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - wchar_t* name; - uint32 attributes; - HANDLE root_directory; - NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, - &root_directory); - if (!NT_SUCCESS(ret) || NULL == name) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_NTOPENKEY_TAG, name, attributes, - root_directory, desired_access, &answer); - - operator delete(name, NT_ALLOC); - - if (SBOX_ALL_OK != code) - break; - - if (!NT_SUCCESS(answer.nt_status)) - // TODO(nsylvain): We should return answer.nt_status here instead - // of status. We can do this only after we checked the policy. - // otherwise we will returns ACCESS_DENIED for all paths - // that are not specified by a policy, even though your token allows - // access to that path, and the original call had a more meaningful - // error. Bug 4369 - break; - - __try { - *key = answer.handle; - status = answer.nt_status; - } __except(EXCEPTION_EXECUTE_HANDLER) { - break; - } - } while (false); - - return status; -} - -NTSTATUS WINAPI TargetNtOpenKey(NtOpenKeyFunction orig_OpenKey, PHANDLE key, - ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes) { - // Check if the process can open it first. - NTSTATUS status = orig_OpenKey(key, desired_access, object_attributes); - if (NT_SUCCESS(status)) - return status; - - return CommonNtOpenKey(status, key, desired_access, object_attributes); -} - -NTSTATUS WINAPI TargetNtOpenKeyEx(NtOpenKeyExFunction orig_OpenKeyEx, - PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, - ULONG open_options) { - // Check if the process can open it first. - NTSTATUS status = orig_OpenKeyEx(key, desired_access, object_attributes, - open_options); - - // We do not support open_options at this time. The 2 current known values - // are REG_OPTION_CREATE_LINK, to open a symbolic link, and - // REG_OPTION_BACKUP_RESTORE to open the key with special privileges. - if (NT_SUCCESS(status) || open_options != 0) - return status; - - return CommonNtOpenKey(status, key, desired_access, object_attributes); -} - -} // namespace sandbox diff --git a/sandbox/src/registry_interception.h b/sandbox/src/registry_interception.h deleted file mode 100644 index 7036c49..0000000 --- a/sandbox/src/registry_interception.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -#ifndef SANDBOX_SRC_REGISTRY_INTERCEPTION_H__ -#define SANDBOX_SRC_REGISTRY_INTERCEPTION_H__ - -namespace sandbox { - -extern "C" { - -// Interception of NtCreateKey on the child process. -// It should never be called directly -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey( - NtCreateKeyFunction orig_CreateKey, PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, ULONG title_index, - PUNICODE_STRING class_name, ULONG create_options, PULONG disposition); - -// Interception of NtOpenKey on the child process. -// It should never be called directly -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey( - NtOpenKeyFunction orig_OpenKey, PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes); - -// Interception of NtOpenKeyEx on the child process. -// It should never be called directly -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx( - NtOpenKeyExFunction orig_OpenKeyEx, PHANDLE key, ACCESS_MASK desired_access, - POBJECT_ATTRIBUTES object_attributes, ULONG open_options); - -} // extern "C" - -} // namespace sandbox - -#endif // SANDBOX_SRC_REGISTRY_INTERCEPTION_H__ diff --git a/sandbox/src/registry_policy.cc b/sandbox/src/registry_policy.cc deleted file mode 100644 index 497636f..0000000 --- a/sandbox/src/registry_policy.cc +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "sandbox/src/registry_policy.h" - -#include "base/logging.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_engine_opcodes.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox_utils.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/win_utils.h" - -namespace { - -static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | - KEY_NOTIFY | KEY_READ | GENERIC_READ | - GENERIC_EXECUTE | READ_CONTROL; - -// Opens the key referenced by |obj_attributes| with |access| and -// checks what permission was given. Remove the WRITE flags and update -// |access| with the new value. -NTSTATUS TranslateMaximumAllowed(OBJECT_ATTRIBUTES* obj_attributes, - DWORD* access) { - NtOpenKeyFunction NtOpenKey = NULL; - ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey); - - NtCloseFunction NtClose = NULL; - ResolveNTFunctionPtr("NtClose", &NtClose); - - NtQueryObjectFunction NtQueryObject = NULL; - ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject); - - // Open the key. - HANDLE handle; - NTSTATUS status = NtOpenKey(&handle, *access, obj_attributes); - if (!NT_SUCCESS(status)) - return status; - - OBJECT_BASIC_INFORMATION info = {0}; - status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info), - NULL); - NtClose(handle); - if (!NT_SUCCESS(status)) - return status; - - *access = info.GrantedAccess & kAllowedRegFlags; - return STATUS_SUCCESS; -} - -NTSTATUS NtCreateKeyInTarget(HANDLE* target_key_handle, - ACCESS_MASK desired_access, - OBJECT_ATTRIBUTES* obj_attributes, - ULONG title_index, - UNICODE_STRING* class_name, - ULONG create_options, - ULONG* disposition, - HANDLE target_process) { - NtCreateKeyFunction NtCreateKey = NULL; - ResolveNTFunctionPtr("NtCreateKey", &NtCreateKey); - - if (MAXIMUM_ALLOWED & desired_access) { - NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access); - if (!NT_SUCCESS(status)) - return STATUS_ACCESS_DENIED; - } - - HANDLE local_handle = INVALID_HANDLE_VALUE; - NTSTATUS status = NtCreateKey(&local_handle, desired_access, obj_attributes, - title_index, class_name, create_options, - disposition); - if (!NT_SUCCESS(status)) - return status; - - if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - target_process, target_key_handle, 0, FALSE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(local_handle); - return STATUS_ACCESS_DENIED; - } - return STATUS_SUCCESS; -} - -NTSTATUS NtOpenKeyInTarget(HANDLE* target_key_handle, - ACCESS_MASK desired_access, - OBJECT_ATTRIBUTES* obj_attributes, - HANDLE target_process) { - NtOpenKeyFunction NtOpenKey = NULL; - ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey); - - if (MAXIMUM_ALLOWED & desired_access) { - NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access); - if (!NT_SUCCESS(status)) - return STATUS_ACCESS_DENIED; - } - - HANDLE local_handle = INVALID_HANDLE_VALUE; - NTSTATUS status = NtOpenKey(&local_handle, desired_access, obj_attributes); - - if (!NT_SUCCESS(status)) - return status; - - if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - target_process, target_key_handle, 0, FALSE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(local_handle); - return STATUS_ACCESS_DENIED; - } - return STATUS_SUCCESS; -} - -} - -namespace sandbox { - -bool RegistryPolicy::GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy) { - std::wstring resovled_name(name); - if (resovled_name.empty()) { - return false; - } - - if (!ResolveRegistryName(resovled_name, &resovled_name)) - return false; - - name = resovled_name.c_str(); - - EvalResult result = ASK_BROKER; - - PolicyRule open(result); - PolicyRule create(result); - - switch (semantics) { - case TargetPolicy::REG_ALLOW_READONLY: { - // We consider all flags that are not known to be readonly as potentially - // used for write. Here we also support MAXIMUM_ALLOWED, but we are going - // to expand it to read-only before the call. - DWORD restricted_flags = ~(kAllowedRegFlags | MAXIMUM_ALLOWED); - open.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); - create.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); - break; - } - case TargetPolicy::REG_ALLOW_ANY: { - break; - } - default: { - NOTREACHED(); - return false; - } - } - - if (!create.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) || - !policy->AddRule(IPC_NTCREATEKEY_TAG, &create)) { - return false; - } - - if (!open.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) || - !policy->AddRule(IPC_NTOPENKEY_TAG, &open)) { - return false; - } - - return true; -} - -bool RegistryPolicy::CreateKeyAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &key, - uint32 attributes, - HANDLE root_directory, - uint32 desired_access, - uint32 title_index, - uint32 create_options, - HANDLE* handle, - NTSTATUS* nt_status, - ULONG* disposition) { - // The only action supported is ASK_BROKER which means create the requested - // file as specified. - if (ASK_BROKER != eval_result) { - *nt_status = STATUS_ACCESS_DENIED; - return false; - } - - // We don't support creating link keys, volatile keys or backup/restore. - if (create_options) { - *nt_status = STATUS_ACCESS_DENIED; - return false; - } - - UNICODE_STRING uni_name = {0}; - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(key, attributes, root_directory, &obj_attributes, - &uni_name); - *nt_status = NtCreateKeyInTarget(handle, desired_access, &obj_attributes, - title_index, NULL, create_options, - disposition, client_info.process); - return true; -} - -bool RegistryPolicy::OpenKeyAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &key, - uint32 attributes, - HANDLE root_directory, - uint32 desired_access, - HANDLE* handle, - NTSTATUS* nt_status) { - // The only action supported is ASK_BROKER which means open the requested - // file as specified. - if (ASK_BROKER != eval_result) { - *nt_status = STATUS_ACCESS_DENIED; - return true; - } - - UNICODE_STRING uni_name = {0}; - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(key, attributes, root_directory, &obj_attributes, - &uni_name); - *nt_status = NtOpenKeyInTarget(handle, desired_access, &obj_attributes, - client_info.process); - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/registry_policy.h b/sandbox/src/registry_policy.h deleted file mode 100644 index da41d1c..0000000 --- a/sandbox/src/registry_policy.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_REGISTRY_POLICY_H__ -#define SANDBOX_SRC_REGISTRY_POLICY_H__ - -#include - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/policy_low_level.h" -#include "sandbox/src/sandbox_policy.h" - -namespace sandbox { - -enum EvalResult; - -// This class centralizes most of the knowledge related to registry policy -class RegistryPolicy { - public: - // Creates the required low-level policy rules to evaluate a high-level - // policy rule for registry IO, in particular open or create actions. - static bool GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy); - - // Performs the desired policy action on a create request with an - // API that is compatible with the IPC-received parameters. - static bool CreateKeyAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &key, - uint32 attributes, - HANDLE root_directory, - uint32 desired_access, - uint32 title_index, - uint32 create_options, - HANDLE* handle, - NTSTATUS* nt_status, - ULONG* disposition); - - // Performs the desired policy action on an open request with an - // API that is compatible with the IPC-received parameters. - static bool OpenKeyAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &key, - uint32 attributes, - HANDLE root_directory, - uint32 desired_access, - HANDLE* handle, - NTSTATUS* nt_status); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_REGISTRY_POLICY_H__ diff --git a/sandbox/src/registry_policy_test.cc b/sandbox/src/registry_policy_test.cc deleted file mode 100644 index cdc1577..0000000 --- a/sandbox/src/registry_policy_test.cc +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "testing/gtest/include/gtest/gtest.h" -#include "sandbox/src/registry_policy.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/win_utils.h" -#include "sandbox/tests/common/controller.h" - -namespace { - -static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | - KEY_NOTIFY | KEY_READ | GENERIC_READ | - GENERIC_EXECUTE | READ_CONTROL; - -#define BINDNTDLL(name) \ - name ## Function name = reinterpret_cast( \ - ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) - -bool IsKeyOpenForRead(HKEY handle) { - BINDNTDLL(NtQueryObject); - - OBJECT_BASIC_INFORMATION info = {0}; - NTSTATUS status = NtQueryObject(handle, ObjectBasicInformation, &info, - sizeof(info), NULL); - - if (!NT_SUCCESS(status)) - return false; - - if ((info.GrantedAccess & (~kAllowedRegFlags)) != 0) - return false; - return true; -} - -} - -namespace sandbox { - -SBOX_TESTS_COMMAND int Reg_OpenKey(int argc, wchar_t **argv) { - if (argc != 4) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - REGSAM desired_access = 0; - ULONG options = 0; - if (wcscmp(argv[1], L"read") == 0) { - desired_access = KEY_READ; - } else if (wcscmp(argv[1], L"write") == 0) { - desired_access = KEY_ALL_ACCESS; - } else if (wcscmp(argv[1], L"link") == 0) { - options = REG_OPTION_CREATE_LINK; - desired_access = KEY_ALL_ACCESS; - } else { - desired_access = MAXIMUM_ALLOWED; - } - - HKEY root = GetReservedKeyFromName(argv[2]); - HKEY key; - LRESULT result = 0; - - if (wcscmp(argv[0], L"create") == 0) - result = ::RegCreateKeyEx(root, argv[3], 0, NULL, options, desired_access, - NULL, &key, NULL); - else - result = ::RegOpenKeyEx(root, argv[3], 0, desired_access, &key); - - if (ERROR_SUCCESS == result) { - if (MAXIMUM_ALLOWED == desired_access) { - if (!IsKeyOpenForRead(key)) { - ::RegCloseKey(key); - return SBOX_TEST_FAILED; - } - } - ::RegCloseKey(key); - return SBOX_TEST_SUCCEEDED; - } else if (ERROR_ACCESS_DENIED == result) { - return SBOX_TEST_DENIED; - } - - return SBOX_TEST_FAILED; -} - -TEST(RegistryPolicyTest, TestKeyAnyAccess) { - TestRunner runner; - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_LOCAL_MACHINE")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_ANY, - L"HKEY_LOCAL_MACHINE\\Software\\Microsoft")); - - // Tests read access on key allowed for read-write. - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( - L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( - L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft")); - - if (::IsUserAnAdmin()) { - // Tests write access on key allowed for read-write. - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( - L"Reg_OpenKey create write HKEY_LOCAL_MACHINE software\\microsoft")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( - L"Reg_OpenKey open write HKEY_LOCAL_MACHINE software\\microsoft")); - } - - // Tests subdirectory access on keys where we don't have subdirectory acess. - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create read " - L"HKEY_LOCAL_MACHINE software\\microsoft\\Windows")); - - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey open read " - L"HKEY_LOCAL_MACHINE software\\microsoft\\windows")); - - // Tests to see if we can create keys where we dont have subdirectory access. - // This is denied. - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create write " - L"HKEY_LOCAL_MACHINE software\\Microsoft\\google_unit_tests")); - - RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Microsoft\\google_unit_tests"); - - // Tests if we need to handle differently the "\\" at the end. - // This is denied. We need to add both rules. - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( - L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft\\")); - - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( - L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft\\")); -} - -TEST(RegistryPolicyTest, TestKeyNoAccess) { - TestRunner runner; - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_LOCAL_MACHINE")); - - // Tests read access where we don't have access at all. - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( - L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software")); - - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( - L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software")); -} - -TEST(RegistryPolicyTest, TestKeyReadOnlyAccess) { - TestRunner runner; - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_LOCAL_MACHINE")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_LOCAL_MACHINE\\Software\\Policies")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*")); - - // Tests subdirectory acess on keys where we have subdirectory acess. - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create read " - L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey open read " - L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft")); - - // Tests to see if we can create keys where we have subdirectory access. - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create write " - L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests")); - - RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests"); -} - -TEST(RegistryPolicyTest, TestKeyAllAccessSubDir) { - TestRunner runner; - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_LOCAL_MACHINE")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_ANY, - L"HKEY_LOCAL_MACHINE\\Software\\Policies")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_ANY, - L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*")); - - if (::IsUserAnAdmin()) { - // Tests to see if we can create keys where we have subdirectory access. - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create write " - L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests")); - - RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests"); - } -} - -TEST(RegistryPolicyTest, TestKeyCreateLink) { - TestRunner runner; - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_LOCAL_MACHINE")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_ANY, - L"HKEY_LOCAL_MACHINE\\Software\\Policies")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_ANY, - L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*")); - - // Tests to see if we can create a registry link key. - // NOTE: In theory here we should make sure to check for SBOX_TEST_DENIED - // instead of !SBOX_TEST_SUCCEEDED, but unfortunately the result is not - // access denied. Internally RegCreateKeyEx (At least on Vista 64) tries to - // create the link, and we return successfully access denied, then, it - // decides to try to break the path in multiple chunks, and create the links - // one by one. In this scenario, it tries to create "HKLM\Software" as a - // link key, which obviously fail with STATUS_OBJECT_NAME_COLLISION, and - // this is what is returned to the user. - EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create link " - L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests")); - - // In case our code fails, and the call works, we need to delete the new - // link. There is no api for this, so we need to use the NT call. - HKEY key = NULL; - LRESULT result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, - L"software\\Policies\\google_unit_tests", - REG_OPTION_OPEN_LINK, MAXIMUM_ALLOWED, - &key); - - if (!result) { - HMODULE ntdll = GetModuleHandle(L"ntdll.dll"); - NtDeleteKeyFunction NtDeleteKey = - reinterpret_cast(GetProcAddress(ntdll, - "NtDeleteKey")); - NtDeleteKey(key); - } -} - -TEST(RegistryPolicyTest, TestKeyReadOnlyHKCU) { - TestRunner runner; - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_CURRENT_USER")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_CURRENT_USER\\Software")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_USERS\\.default")); - - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_READONLY, - L"HKEY_USERS\\.default\\software")); - - // Tests read access where we only have read-only access. - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( - L"Reg_OpenKey create read HKEY_CURRENT_USER software")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( - L"Reg_OpenKey open read HKEY_CURRENT_USER software")); - - // Tests write access where we only have read-only acess. - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( - L"Reg_OpenKey create write HKEY_CURRENT_USER software")); - - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( - L"Reg_OpenKey open write HKEY_CURRENT_USER software")); - - // Tests maximum allowed access where we only have read-only access. - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( - L"Reg_OpenKey create maximum_allowed HKEY_CURRENT_USER software")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( - L"Reg_OpenKey open maximum_allowed HKEY_CURRENT_USER software")); -} - -} // namespace sandbox diff --git a/sandbox/src/resolver.cc b/sandbox/src/resolver.cc deleted file mode 100644 index 295bfb2..0000000 --- a/sandbox/src/resolver.cc +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/resolver.h" - -#include "base/win/pe_image.h" -#include "sandbox/src/sandbox_nt_util.h" - -namespace sandbox { - -NTSTATUS ResolverThunk::Init(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes) { - if (NULL == thunk_storage || 0 == storage_bytes || - NULL == target_module || NULL == target_name) - return STATUS_INVALID_PARAMETER; - - if (storage_bytes < GetThunkSize()) - return STATUS_BUFFER_TOO_SMALL; - - NTSTATUS ret = STATUS_SUCCESS; - if (NULL == interceptor_entry_point) { - ret = ResolveInterceptor(interceptor_module, interceptor_name, - &interceptor_entry_point); - if (!NT_SUCCESS(ret)) - return ret; - } - - ret = ResolveTarget(target_module, target_name, &target_); - if (!NT_SUCCESS(ret)) - return ret; - - interceptor_ = interceptor_entry_point; - - return ret; -} - -NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module, - const char* interceptor_name, - const void** address) { - DCHECK_NT(address); - if (!interceptor_module) - return STATUS_INVALID_PARAMETER; - - base::win::PEImage pe(interceptor_module); - if (!pe.VerifyMagic()) - return STATUS_INVALID_IMAGE_FORMAT; - - *address = pe.GetProcAddress(interceptor_name); - - if (!(*address)) - return STATUS_PROCEDURE_NOT_FOUND; - - return STATUS_SUCCESS; -} - -} // namespace sandbox diff --git a/sandbox/src/resolver.h b/sandbox/src/resolver.h deleted file mode 100644 index 7ec0bf5..0000000 --- a/sandbox/src/resolver.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Defines ResolverThunk, the interface for classes that perform interceptions. -// For more details see -// http://dev.chromium.org/developers/design-documents/sandbox . - -#include "base/basictypes.h" -#include "sandbox/src/nt_internals.h" - -#ifndef SANDBOX_SRC_RESOLVER_H__ -#define SANDBOX_SRC_RESOLVER_H__ - -namespace sandbox { - -// A resolver is the object in charge of performing the actual interception of -// a function. There should be a concrete implementation of a resolver roughly -// per type of interception. -class ResolverThunk { - public: - ResolverThunk() {} - virtual ~ResolverThunk() {} - - // Performs the actual interception of a function. - // target_name is an exported function from the module loaded at - // target_module, and must be replaced by interceptor_name, exported from - // interceptor_module. interceptor_entry_point can be provided instead of - // interceptor_name / interceptor_module. - // thunk_storage must point to a buffer on the child's address space, to hold - // the patch thunk, and related data. If provided, storage_used will receive - // the number of bytes used from thunk_storage. - // - // Example: (without error checking) - // - // size_t size = resolver.GetThunkSize(); - // char* buffer = ::VirtualAllocEx(child_process, NULL, size, - // MEM_COMMIT, PAGE_READWRITE); - // resolver.Setup(ntdll_module, NULL, L"NtCreateFile", NULL, - // &MyReplacementFunction, buffer, size, NULL); - // - // In general, the idea is to allocate a single big buffer for all - // interceptions on the same dll, and call Setup n times. - // WARNING: This means that any data member that is specific to a single - // interception must be reset within this method. - virtual NTSTATUS Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) = 0; - - // Gets the address of function_name inside module (main exe). - virtual NTSTATUS ResolveInterceptor(const void* module, - const char* function_name, - const void** address); - - // Gets the address of an exported function_name inside module. - virtual NTSTATUS ResolveTarget(const void* module, - const char* function_name, - void** address); - - // Gets the required buffer size for this type of thunk. - virtual size_t GetThunkSize() const = 0; - - protected: - // Performs basic initialization on behalf of a concrete instance of a - // resolver. That is, parameter validation and resolution of the target - // and the interceptor into the member variables. - // - // target_name is an exported function from the module loaded at - // target_module, and must be replaced by interceptor_name, exported from - // interceptor_module. interceptor_entry_point can be provided instead of - // interceptor_name / interceptor_module. - // thunk_storage must point to a buffer on the child's address space, to hold - // the patch thunk, and related data. - virtual NTSTATUS Init(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes); - - // Gets the required buffer size for the internal part of the thunk. - size_t GetInternalThunkSize() const; - - // Initializes the internal part of the thunk. - // interceptor is the function to be called instead of original_function. - bool SetInternalThunk(void* storage, size_t storage_bytes, - const void* original_function, const void* interceptor); - - // Holds the resolved interception target. - void* target_; - // Holds the resolved interception interceptor. - const void* interceptor_; - - DISALLOW_COPY_AND_ASSIGN(ResolverThunk); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_RESOLVER_H__ diff --git a/sandbox/src/resolver_32.cc b/sandbox/src/resolver_32.cc deleted file mode 100644 index ae5c168..0000000 --- a/sandbox/src/resolver_32.cc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/resolver.h" - -#include "sandbox/src/sandbox_nt_util.h" - -namespace { - -#pragma pack(push, 1) -struct InternalThunk { - // This struct contains roughly the following code: - // sub esp, 8 // Create working space - // push edx // Save register - // mov edx, [esp + 0xc] // Get return adddress - // mov [esp + 8], edx // Store return address - // mov dword ptr [esp + 0xc], 0x7c401200 // Store extra argument - // mov dword ptr [esp + 4], 0x40010203 // Store address to jump to - // pop edx // Restore register - // ret // Jump to interceptor - // - // This code only modifies esp and eip so it must work with to normal calling - // convention. It is assembled as: - // - // 00 83ec08 sub esp,8 - // 03 52 push edx - // 04 8b54240c mov edx,dword ptr [esp + 0Ch] - // 08 89542408 mov dword ptr [esp + 8], edx - // 0c c744240c0012407c mov dword ptr [esp + 0Ch], 7C401200h - // 14 c744240403020140 mov dword ptr [esp + 4], 40010203h - // 1c 5a pop edx - // 1d c3 ret - InternalThunk() { - opcodes_1 = 0x5208ec83; - opcodes_2 = 0x0c24548b; - opcodes_3 = 0x08245489; - opcodes_4 = 0x0c2444c7; - opcodes_5 = 0x042444c7; - opcodes_6 = 0xc35a; - extra_argument = 0; - interceptor_function = 0; - }; - ULONG opcodes_1; // = 0x5208ec83 - ULONG opcodes_2; // = 0x0c24548b - ULONG opcodes_3; // = 0x08245489 - ULONG opcodes_4; // = 0x0c2444c7 - ULONG extra_argument; - ULONG opcodes_5; // = 0x042444c7 - ULONG interceptor_function; - USHORT opcodes_6; // = 0xc35a -}; -#pragma pack(pop) - -}; // namespace - -namespace sandbox { - -bool ResolverThunk::SetInternalThunk(void* storage, size_t storage_bytes, - const void* original_function, - const void* interceptor) { - if (storage_bytes < sizeof(InternalThunk)) - return false; - - InternalThunk* thunk = new(storage, NT_PLACE) InternalThunk; - -#pragma warning(push) -#pragma warning(disable: 4311) - // These casts generate warnings because they are 32 bit specific. - thunk->interceptor_function = reinterpret_cast(interceptor); - thunk->extra_argument = reinterpret_cast(original_function); -#pragma warning(pop) - - return true; -} - -size_t ResolverThunk::GetInternalThunkSize() const { - return sizeof(InternalThunk); -} - -NTSTATUS ResolverThunk::ResolveTarget(const void* module, - const char* function_name, - void** address) { - const void** casted = const_cast(address); - return ResolverThunk::ResolveInterceptor(module, function_name, casted); -} - -} // namespace sandbox diff --git a/sandbox/src/resolver_64.cc b/sandbox/src/resolver_64.cc deleted file mode 100644 index 96d039b..0000000 --- a/sandbox/src/resolver_64.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/resolver.h" - -#include "sandbox/src/sandbox_nt_util.h" - -namespace { - -const BYTE kPushRax = 0x50; -const USHORT kMovRax = 0xB848; -const ULONG kMovRspRax = 0x24048948; -const BYTE kRetNp = 0xC3; - -#pragma pack(push, 1) -struct InternalThunk { - // This struct contains roughly the following code: - // 00 50 push rax - // 01 48b8f0debc9a78563412 mov rax,123456789ABCDEF0h - // 0b 48890424 mov qword ptr [rsp],rax - // 0f c3 ret - // - // The code modifies rax, but that should not be an issue for the common - // calling conventions. - - InternalThunk() { - push_rax = kPushRax; - mov_rax = kMovRax; - interceptor_function = 0; - mov_rsp_rax = kMovRspRax; - ret = kRetNp; - }; - BYTE push_rax; // = 50 - USHORT mov_rax; // = 48 B8 - ULONG_PTR interceptor_function; - ULONG mov_rsp_rax; // = 48 89 04 24 - BYTE ret; // = C3 -}; -#pragma pack(pop) - -} // namespace. - -namespace sandbox { - -size_t ResolverThunk::GetInternalThunkSize() const { - return sizeof(InternalThunk); -} - -bool ResolverThunk::SetInternalThunk(void* storage, size_t storage_bytes, - const void* original_function, - const void* interceptor) { - if (storage_bytes < sizeof(InternalThunk)) - return false; - - InternalThunk* thunk = new(storage, NT_PLACE) InternalThunk; - thunk->interceptor_function = reinterpret_cast(interceptor); - - return true; -} - -NTSTATUS ResolverThunk::ResolveTarget(const void* module, - const char* function_name, - void** address) { - // We don't support sidestep & co. - return STATUS_NOT_IMPLEMENTED; -} - -} // namespace sandbox diff --git a/sandbox/src/restricted_token.cc b/sandbox/src/restricted_token.cc deleted file mode 100644 index bac8816..0000000 --- a/sandbox/src/restricted_token.cc +++ /dev/null @@ -1,466 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/restricted_token.h" - -#include - -#include "base/logging.h" -#include "sandbox/src/acl.h" -#include "sandbox/src/win_utils.h" - - -namespace sandbox { - -unsigned RestrictedToken::Init(const HANDLE effective_token) { - DCHECK(!init_); - if (init_) - return ERROR_ALREADY_INITIALIZED; - - if (effective_token) { - // We duplicate the handle to be able to use it even if the original handle - // is closed. - HANDLE effective_token_dup; - if (::DuplicateHandle(::GetCurrentProcess(), - effective_token, - ::GetCurrentProcess(), - &effective_token_dup, - DUPLICATE_SAME_ACCESS, - FALSE, - 0)) { // no special options - effective_token_ = effective_token_dup; - } else { - return ::GetLastError(); - } - } else { - if (!::OpenProcessToken(::GetCurrentProcess(), - TOKEN_ALL_ACCESS, - &effective_token_)) - return ::GetLastError(); - } - - init_ = true; - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::GetRestrictedTokenHandle(HANDLE *token_handle) const { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - size_t deny_size = sids_for_deny_only_.size(); - size_t restrict_size = sids_to_restrict_.size(); - size_t privileges_size = privileges_to_disable_.size(); - - SID_AND_ATTRIBUTES *deny_only_array = NULL; - if (deny_size) { - deny_only_array = new SID_AND_ATTRIBUTES[deny_size]; - - for (unsigned int i = 0; i < sids_for_deny_only_.size() ; ++i) { - deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY; - deny_only_array[i].Sid = - const_cast(sids_for_deny_only_[i].GetPSID()); - } - } - - SID_AND_ATTRIBUTES *sids_to_restrict_array = NULL; - if (restrict_size) { - sids_to_restrict_array = new SID_AND_ATTRIBUTES[restrict_size]; - - for (unsigned int i = 0; i < restrict_size; ++i) { - sids_to_restrict_array[i].Attributes = 0; - sids_to_restrict_array[i].Sid = - const_cast(sids_to_restrict_[i].GetPSID()); - } - } - - LUID_AND_ATTRIBUTES *privileges_to_disable_array = NULL; - if (privileges_size) { - privileges_to_disable_array = new LUID_AND_ATTRIBUTES[privileges_size]; - - for (unsigned int i = 0; i < privileges_size; ++i) { - privileges_to_disable_array[i].Attributes = 0; - privileges_to_disable_array[i].Luid = privileges_to_disable_[i]; - } - } - - BOOL result = TRUE; - HANDLE new_token = NULL; - // The SANDBOX_INERT flag did nothing in XP and it was just a way to tell - // if a token has ben restricted given the limiations of IsTokenRestricted() - // but it appears that in Windows 7 it hints the AppLocker subsystem to - // leave us alone. - if (deny_size || restrict_size || privileges_size) { - result = ::CreateRestrictedToken(effective_token_, - SANDBOX_INERT, - static_cast(deny_size), - deny_only_array, - static_cast(privileges_size), - privileges_to_disable_array, - static_cast(restrict_size), - sids_to_restrict_array, - &new_token); - } else { - // Duplicate the token even if it's not modified at this point - // because any subsequent changes to this token would also affect the - // current process. - result = ::DuplicateTokenEx(effective_token_, TOKEN_ALL_ACCESS, NULL, - SecurityIdentification, TokenPrimary, - &new_token); - } - - if (deny_only_array) - delete[] deny_only_array; - - if (sids_to_restrict_array) - delete[] sids_to_restrict_array; - - if (privileges_to_disable_array) - delete[] privileges_to_disable_array; - - if (!result) - return ::GetLastError(); - - // Modify the default dacl on the token to contain Restricted and the user. - if (!AddSidToDefaultDacl(new_token, WinRestrictedCodeSid, GENERIC_ALL)) - return ::GetLastError(); - - if (!AddUserSidToDefaultDacl(new_token, GENERIC_ALL)) - return ::GetLastError(); - - DWORD error = SetTokenIntegrityLevel(new_token, integrity_level_); - if (ERROR_SUCCESS != error) - return error; - - BOOL status = ::DuplicateHandle(::GetCurrentProcess(), - new_token, - ::GetCurrentProcess(), - token_handle, - TOKEN_ALL_ACCESS, - FALSE, // Don't inherit. - 0); - - if (new_token != effective_token_) - ::CloseHandle(new_token); - - if (!status) - return ::GetLastError(); - - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::GetRestrictedTokenHandleForImpersonation( - HANDLE *token_handle) const { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - HANDLE restricted_token_handle; - unsigned err_code = GetRestrictedTokenHandle(&restricted_token_handle); - if (ERROR_SUCCESS != err_code) - return err_code; - - HANDLE impersonation_token; - if (!::DuplicateToken(restricted_token_handle, - SecurityImpersonation, - &impersonation_token)) { - ::CloseHandle(restricted_token_handle); - return ::GetLastError(); - } - - ::CloseHandle(restricted_token_handle); - - BOOL status = ::DuplicateHandle(::GetCurrentProcess(), - impersonation_token, - ::GetCurrentProcess(), - token_handle, - TOKEN_ALL_ACCESS, - FALSE, // Don't inherit. - 0); - - ::CloseHandle(impersonation_token); - - if (!status) - return ::GetLastError(); - - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::AddAllSidsForDenyOnly(std::vector *exceptions) { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - TOKEN_GROUPS *token_groups = NULL; - DWORD size = 0; - - BOOL result = ::GetTokenInformation(effective_token_, - TokenGroups, - NULL, // No buffer. - 0, // Size is 0. - &size); - if (!size) - return ::GetLastError(); - - token_groups = reinterpret_cast(new BYTE[size]); - result = ::GetTokenInformation(effective_token_, - TokenGroups, - token_groups, - size, - &size); - if (!result) { - delete[] reinterpret_cast(token_groups); - return ::GetLastError(); - } - - // Build the list of the deny only group SIDs - for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { - if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 && - (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) { - bool should_ignore = false; - if (exceptions) { - for (unsigned int j = 0; j < exceptions->size(); ++j) { - if (::EqualSid(const_cast((*exceptions)[j].GetPSID()), - token_groups->Groups[i].Sid)) { - should_ignore = true; - break; - } - } - } - if (!should_ignore) { - sids_for_deny_only_.push_back( - reinterpret_cast(token_groups->Groups[i].Sid)); - } - } - } - - delete[] reinterpret_cast(token_groups); - - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::AddSidForDenyOnly(const Sid &sid) { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - sids_for_deny_only_.push_back(sid); - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::AddUserSidForDenyOnly() { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; - TOKEN_USER* token_user = reinterpret_cast(new BYTE[size]); - - BOOL result = ::GetTokenInformation(effective_token_, - TokenUser, - token_user, - size, - &size); - - Sid user = reinterpret_cast(token_user->User.Sid); - delete[] reinterpret_cast(token_user); - - if (!result) - return ::GetLastError(); - - sids_for_deny_only_.push_back(user); - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::DeleteAllPrivileges( - const std::vector *exceptions) { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - // Get the list of privileges in the token - TOKEN_PRIVILEGES *token_privileges = NULL; - DWORD size = 0; - - BOOL result = ::GetTokenInformation(effective_token_, - TokenPrivileges, - NULL, // No buffer. - 0, // Size is 0. - &size); - if (!size) - return ::GetLastError(); - - token_privileges = reinterpret_cast(new BYTE[size]); - result = ::GetTokenInformation(effective_token_, - TokenPrivileges, - token_privileges, - size, - &size); - if (!result) { - delete[] reinterpret_cast(token_privileges); - return ::GetLastError(); - } - - - // Build the list of privileges to disable - for (unsigned int i = 0; i < token_privileges->PrivilegeCount; ++i) { - bool should_ignore = false; - if (exceptions) { - for (unsigned int j = 0; j < exceptions->size(); ++j) { - LUID luid = {0}; - ::LookupPrivilegeValue(NULL, (*exceptions)[j].c_str(), &luid); - if (token_privileges->Privileges[i].Luid.HighPart == luid.HighPart && - token_privileges->Privileges[i].Luid.LowPart == luid.LowPart) { - should_ignore = true; - break; - } - } - } - if (!should_ignore) { - privileges_to_disable_.push_back(token_privileges->Privileges[i].Luid); - } - } - - delete[] reinterpret_cast(token_privileges); - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::DeletePrivilege(const wchar_t *privilege) { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - LUID luid = {0}; - if (LookupPrivilegeValue(NULL, privilege, &luid)) - privileges_to_disable_.push_back(luid); - else - return ::GetLastError(); - - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::AddRestrictingSid(const Sid &sid) { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - sids_to_restrict_.push_back(sid); // No attributes - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::AddRestrictingSidLogonSession() { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - TOKEN_GROUPS *token_groups = NULL; - DWORD size = 0; - - BOOL result = ::GetTokenInformation(effective_token_, - TokenGroups, - NULL, // No buffer. - 0, // Size is 0. - &size); - if (!size) - return ::GetLastError(); - - token_groups = reinterpret_cast(new BYTE[size]); - result = ::GetTokenInformation(effective_token_, - TokenGroups, - token_groups, - size, - &size); - if (!result) { - delete[] reinterpret_cast(token_groups); - return ::GetLastError(); - } - - SID *logon_sid = NULL; - for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { - if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) { - logon_sid = static_cast(token_groups->Groups[i].Sid); - break; - } - } - - if (logon_sid) - sids_to_restrict_.push_back(logon_sid); - - delete[] reinterpret_cast(token_groups); - - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::AddRestrictingSidCurrentUser() { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; - TOKEN_USER* token_user = reinterpret_cast(new BYTE[size]); - - BOOL result = ::GetTokenInformation(effective_token_, - TokenUser, - token_user, - size, - &size); - - Sid user = reinterpret_cast(token_user->User.Sid); - delete[] reinterpret_cast(token_user); - - - if (!result) - return ::GetLastError(); - - sids_to_restrict_.push_back(user); - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::AddRestrictingSidAllSids() { - DCHECK(init_); - if (!init_) - return ERROR_NO_TOKEN; - - // Add the current user to the list. - unsigned error = AddRestrictingSidCurrentUser(); - if (ERROR_SUCCESS != error) - return error; - - TOKEN_GROUPS *token_groups = NULL; - DWORD size = 0; - - // Get the buffer size required. - BOOL result = ::GetTokenInformation(effective_token_, TokenGroups, NULL, 0, - &size); - if (!size) - return ::GetLastError(); - - token_groups = reinterpret_cast(new BYTE[size]); - result = ::GetTokenInformation(effective_token_, - TokenGroups, - token_groups, - size, - &size); - if (!result) { - delete[] reinterpret_cast(token_groups); - return ::GetLastError(); - } - - // Build the list of restricting sids from all groups. - for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { - if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0) - AddRestrictingSid(reinterpret_cast(token_groups->Groups[i].Sid)); - } - - delete[] reinterpret_cast(token_groups); - - return ERROR_SUCCESS; -} - -unsigned RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) { - integrity_level_ = integrity_level; - return ERROR_SUCCESS; -} - -} // namespace sandbox diff --git a/sandbox/src/restricted_token.h b/sandbox/src/restricted_token.h deleted file mode 100644 index 88bd70f..0000000 --- a/sandbox/src/restricted_token.h +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_RESTRICTED_TOKEN_H_ -#define SANDBOX_SRC_RESTRICTED_TOKEN_H_ - -#include -#include - -#include "base/basictypes.h" -#include "sandbox/src/restricted_token_utils.h" -#include "sandbox/src/security_level.h" -#include "sandbox/src/sid.h" - -// Flags present in the Group SID list. These 2 flags are new in Windows Vista -#ifndef SE_GROUP_INTEGRITY -#define SE_GROUP_INTEGRITY (0x00000020L) -#endif -#ifndef SE_GROUP_INTEGRITY_ENABLED -#define SE_GROUP_INTEGRITY_ENABLED (0x00000040L) -#endif - -namespace sandbox { - -// Handles the creation of a restricted token using the effective token or -// any token handle. -// Sample usage: -// RestrictedToken restricted_token; -// unsigned err_code = restricted_token.Init(NULL); // Use the current -// // effective token -// if (ERROR_SUCCESS != err_code) { -// // handle error. -// } -// -// restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID()); -// HANDLE token_handle; -// err_code = restricted_token.GetRestrictedTokenHandle(&token_handle); -// if (ERROR_SUCCESS != err_code) { -// // handle error. -// } -// [...] -// CloseHandle(token_handle); -class RestrictedToken { - public: - // Init() has to be called before calling any other method in the class. - RestrictedToken() - : init_(false), effective_token_(NULL), - integrity_level_(INTEGRITY_LEVEL_LAST) { } - - ~RestrictedToken() { - if (effective_token_) - CloseHandle(effective_token_); - } - - // Initializes the RestrictedToken object with effective_token. - // If effective_token is NULL, it initializes the RestrictedToken object with - // the effective token of the current process. - unsigned Init(HANDLE effective_token); - - // Creates a restricted token and returns its handle using the token_handle - // output parameter. This handle has to be closed by the caller. - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - unsigned GetRestrictedTokenHandle(HANDLE *token_handle) const; - - // Creates a restricted token and uses this new token to create a new token - // for impersonation. Returns the handle of this impersonation token using - // the token_handle output parameter. This handle has to be closed by - // the caller. - // - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - // - // The sample usage is the same as the GetRestrictedTokenHandle function. - unsigned GetRestrictedTokenHandleForImpersonation(HANDLE *token_handle) const; - - // Lists all sids in the token and mark them as Deny Only except for those - // present in the exceptions parameter. If there is no exception needed, - // the caller can pass an empty list or NULL for the exceptions - // parameter. - // - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - // - // Sample usage: - // std::vector sid_exceptions; - // sid_exceptions.push_back(ATL::Sids::Users().GetPSID()); - // sid_exceptions.push_back(ATL::Sids::World().GetPSID()); - // restricted_token.AddAllSidsForDenyOnly(&sid_exceptions); - // Note: A Sid marked for Deny Only in a token cannot be used to grant - // access to any resource. It can only be used to deny access. - unsigned AddAllSidsForDenyOnly(std::vector *exceptions); - - // Adds a user or group SID for Deny Only in the restricted token. - // Parameter: sid is the SID to add in the Deny Only list. - // The return value is always ERROR_SUCCESS. - // - // Sample Usage: - // restricted_token.AddSidForDenyOnly(ATL::Sids::Admins().GetPSID()); - unsigned AddSidForDenyOnly(const Sid &sid); - - // Adds the user sid of the token for Deny Only in the restricted token. - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - unsigned AddUserSidForDenyOnly(); - - // Lists all privileges in the token and add them to the list of privileges - // to remove except for those present in the exceptions parameter. If - // there is no exception needed, the caller can pass an empty list or NULL - // for the exceptions parameter. - // - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - // - // Sample usage: - // std::vector privilege_exceptions; - // privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); - // restricted_token.DeleteAllPrivileges(&privilege_exceptions); - unsigned DeleteAllPrivileges( - const std::vector *exceptions); - - // Adds a privilege to the list of privileges to remove in the restricted - // token. - // Parameter: privilege is the privilege name to remove. This is the string - // representing the privilege. (e.g. "SeChangeNotifyPrivilege"). - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - // - // Sample usage: - // restricted_token.DeletePrivilege(SE_LOAD_DRIVER_NAME); - unsigned DeletePrivilege(const wchar_t *privilege); - - // Adds a SID to the list of restricting sids in the restricted token. - // Parameter: sid is the sid to add to the list restricting sids. - // The return value is always ERROR_SUCCESS. - // - // Sample usage: - // restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID()); - // Note: The list of restricting is used to force Windows to perform all - // access checks twice. The first time using your user SID and your groups, - // and the second time using your list of restricting sids. The access has - // to be granted in both places to get access to the resource requested. - unsigned AddRestrictingSid(const Sid &sid); - - // Adds the logon sid of the token in the list of restricting sids for the - // restricted token. - // - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - unsigned AddRestrictingSidLogonSession(); - - // Adds the owner sid of the token in the list of restricting sids for the - // restricted token. - // - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - unsigned AddRestrictingSidCurrentUser(); - - // Adds all group sids and the user sid to the restricting sids list. - // - // If the function succeeds, the return value is ERROR_SUCCESS. If the - // function fails, the return value is the win32 error code corresponding to - // the error. - unsigned AddRestrictingSidAllSids(); - - // Sets the token integrity level. This is only valid on Vista. The integrity - // level cannot be higher than your current integrity level. - unsigned SetIntegrityLevel(IntegrityLevel integrity_level); - - private: - // The list of restricting sids in the restricted token. - std::vector sids_to_restrict_; - // The list of privileges to remove in the restricted token. - std::vector privileges_to_disable_; - // The list of sids to mark as Deny Only in the restricted token. - std::vector sids_for_deny_only_; - // The token to restrict. Can only be set in a constructor. - HANDLE effective_token_; - // The token integrity level. Only valid on Vista. - IntegrityLevel integrity_level_; - // Tells if the object is initialized or not (if Init() has been called) - bool init_; - - DISALLOW_COPY_AND_ASSIGN(RestrictedToken); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_RESTRICTED_TOKEN_H_ diff --git a/sandbox/src/restricted_token_unittest.cc b/sandbox/src/restricted_token_unittest.cc deleted file mode 100644 index 310b73f..0000000 --- a/sandbox/src/restricted_token_unittest.cc +++ /dev/null @@ -1,530 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains unit tests for the RestrictedToken. - -#define _ATL_NO_EXCEPTIONS -#include -#include -#include -#include "sandbox/src/restricted_token.h" -#include "sandbox/src/sid.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -// Tests the initializatioin with an invalid token handle. -TEST(RestrictedTokenTest, InvalidHandle) { - RestrictedToken token; - ASSERT_EQ(ERROR_INVALID_HANDLE, token.Init(reinterpret_cast(0x5555))); -} - -// Tests the initialization with NULL as parameter. -TEST(RestrictedTokenTest, DefaultInit) { - // Get the current process token. - HANDLE token_handle = INVALID_HANDLE_VALUE; - ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, - &token_handle)); - - ASSERT_NE(INVALID_HANDLE_VALUE, token_handle); - - ATL::CAccessToken access_token; - access_token.Attach(token_handle); - - // Create the token using the current token. - RestrictedToken token_default; - ASSERT_EQ(ERROR_SUCCESS, token_default.Init(NULL)); - - // Get the handle to the restricted token. - - HANDLE restricted_token_handle = NULL; - ASSERT_EQ(ERROR_SUCCESS, - token_default.GetRestrictedTokenHandle(&restricted_token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(restricted_token_handle); - - ATL::CSid sid_user_restricted; - ATL::CSid sid_user_default; - ATL::CSid sid_owner_restricted; - ATL::CSid sid_owner_default; - ASSERT_TRUE(restricted_token.GetUser(&sid_user_restricted)); - ASSERT_TRUE(access_token.GetUser(&sid_user_default)); - ASSERT_TRUE(restricted_token.GetOwner(&sid_owner_restricted)); - ASSERT_TRUE(access_token.GetOwner(&sid_owner_default)); - - // Check if both token have the same owner and user. - ASSERT_EQ(sid_user_restricted, sid_user_default); - ASSERT_EQ(sid_owner_restricted, sid_owner_default); -} - -// Tests the initialization with a custom token as parameter. -TEST(RestrictedTokenTest, CustomInit) { - // Get the current process token. - HANDLE token_handle = INVALID_HANDLE_VALUE; - ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, - &token_handle)); - - ASSERT_NE(INVALID_HANDLE_VALUE, token_handle); - - ATL::CAccessToken access_token; - access_token.Attach(token_handle); - - // Change the primary group. - access_token.SetPrimaryGroup(ATL::Sids::World()); - - // Create the token using the current token. - RestrictedToken token; - ASSERT_EQ(ERROR_SUCCESS, token.Init(access_token.GetHandle())); - - // Get the handle to the restricted token. - - HANDLE restricted_token_handle = NULL; - ASSERT_EQ(ERROR_SUCCESS, - token.GetRestrictedTokenHandle(&restricted_token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(restricted_token_handle); - - ATL::CSid sid_restricted; - ATL::CSid sid_default; - ASSERT_TRUE(restricted_token.GetPrimaryGroup(&sid_restricted)); - ASSERT_TRUE(access_token.GetPrimaryGroup(&sid_default)); - - // Check if both token have the same owner. - ASSERT_EQ(sid_restricted, sid_default); -} - -// Verifies that the token created by the object are valid. -TEST(RestrictedTokenTest, ResultToken) { - RestrictedToken token; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - - ASSERT_EQ(ERROR_SUCCESS, - token.AddRestrictingSid(ATL::Sids::World().GetPSID())); - - HANDLE restricted_token; - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&restricted_token)); - - ASSERT_TRUE(::IsTokenRestricted(restricted_token)); - - DWORD length = 0; - TOKEN_TYPE type; - ASSERT_TRUE(::GetTokenInformation(restricted_token, - ::TokenType, - &type, - sizeof(type), - &length)); - - ASSERT_EQ(type, TokenPrimary); - - HANDLE impersonation_token; - ASSERT_EQ(ERROR_SUCCESS, - token.GetRestrictedTokenHandleForImpersonation(&impersonation_token)); - - ASSERT_TRUE(::IsTokenRestricted(impersonation_token)); - - ASSERT_TRUE(::GetTokenInformation(impersonation_token, - ::TokenType, - &type, - sizeof(type), - &length)); - - ASSERT_EQ(type, TokenImpersonation); - - ::CloseHandle(impersonation_token); - ::CloseHandle(restricted_token); -} - -// Verifies that the token created has "Restricted" in its default dacl. -TEST(RestrictedTokenTest, DefaultDacl) { - RestrictedToken token; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - - ASSERT_EQ(ERROR_SUCCESS, - token.AddRestrictingSid(ATL::Sids::World().GetPSID())); - - HANDLE handle; - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(handle); - - ATL::CDacl dacl; - ASSERT_TRUE(restricted_token.GetDefaultDacl(&dacl)); - - bool restricted_found = false; - - unsigned int ace_count = dacl.GetAceCount(); - for (unsigned int i = 0; i < ace_count ; ++i) { - ATL::CSid sid; - ACCESS_MASK mask = 0; - dacl.GetAclEntry(i, &sid, &mask); - if (sid == ATL::Sids::RestrictedCode() && mask == GENERIC_ALL) { - restricted_found = true; - break; - } - } - - ASSERT_TRUE(restricted_found); -} - -// Tests the method "AddSidForDenyOnly". -TEST(RestrictedTokenTest, DenySid) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddSidForDenyOnly(Sid(WinWorldSid))); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - - ATL::CTokenGroups groups; - ASSERT_TRUE(restricted_token.GetGroups(&groups)); - - ATL::CSid::CSidArray sids; - ATL::CAtlArray attributes; - groups.GetSidsAndAttributes(&sids, &attributes); - - for (unsigned int i = 0; i < sids.GetCount(); i++) { - if (ATL::Sids::World() == sids[i]) { - ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, - attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); - } - } -} - -// Tests the method "AddAllSidsForDenyOnly". -TEST(RestrictedTokenTest, DenySids) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddAllSidsForDenyOnly(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - - ATL::CTokenGroups groups; - ASSERT_TRUE(restricted_token.GetGroups(&groups)); - - ATL::CSid::CSidArray sids; - ATL::CAtlArray attributes; - groups.GetSidsAndAttributes(&sids, &attributes); - - // Verify that all sids are really gone. - for (unsigned int i = 0; i < sids.GetCount(); i++) { - if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 && - (attributes[i] & SE_GROUP_INTEGRITY) == 0) { - ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, - attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); - } - } -} - -// Tests the method "AddAllSidsForDenyOnly" using an exception list. -TEST(RestrictedTokenTest, DenySidsException) { - RestrictedToken token; - HANDLE token_handle = NULL; - - std::vector sids_exception; - sids_exception.push_back(Sid(WinWorldSid)); - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddAllSidsForDenyOnly(&sids_exception)); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - - ATL::CTokenGroups groups; - ASSERT_TRUE(restricted_token.GetGroups(&groups)); - - ATL::CSid::CSidArray sids; - ATL::CAtlArray attributes; - groups.GetSidsAndAttributes(&sids, &attributes); - - // Verify that all sids are really gone. - for (unsigned int i = 0; i < sids.GetCount(); i++) { - if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 && - (attributes[i] & SE_GROUP_INTEGRITY) == 0) { - if (ATL::Sids::World() == sids[i]) { - ASSERT_EQ(NULL, attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); - } else { - ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, - attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); - } - } - } -} - -// Tests test method AddOwnerSidForDenyOnly. -TEST(RestrictedTokenTest, DenyOwnerSid) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddUserSidForDenyOnly()); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - - ATL::CTokenGroups groups; - ASSERT_TRUE(restricted_token.GetGroups(&groups)); - - ATL::CSid::CSidArray sids; - ATL::CAtlArray attributes; - groups.GetSidsAndAttributes(&sids, &attributes); - - ATL::CSid user_sid; - ASSERT_TRUE(restricted_token.GetUser(&user_sid)); - - for (unsigned int i = 0; i < sids.GetCount(); ++i) { - if (user_sid == sids[i]) { - ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, - attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); - } - } -} - -// Tests the method DeleteAllPrivileges. -TEST(RestrictedTokenTest, DeleteAllPrivileges) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.DeleteAllPrivileges(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - - ATL::CTokenPrivileges privileges; - ASSERT_TRUE(restricted_token.GetPrivileges(&privileges)); - - ASSERT_EQ(0, privileges.GetCount()); -} - -// Tests the method DeleteAllPrivileges with an exception list. -TEST(RestrictedTokenTest, DeleteAllPrivilegesException) { - RestrictedToken token; - HANDLE token_handle = NULL; - - std::vector exceptions; - exceptions.push_back(SE_CHANGE_NOTIFY_NAME); - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.DeleteAllPrivileges(&exceptions)); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - - ATL::CTokenPrivileges privileges; - ASSERT_TRUE(restricted_token.GetPrivileges(&privileges)); - - ATL::CTokenPrivileges::CNames privilege_names; - ATL::CTokenPrivileges::CAttributes privilege_name_attributes; - privileges.GetNamesAndAttributes(&privilege_names, - &privilege_name_attributes); - - ASSERT_EQ(1, privileges.GetCount()); - - for (unsigned int i = 0; i < privileges.GetCount(); ++i) { - ASSERT_EQ(privilege_names[i], SE_CHANGE_NOTIFY_NAME); - } -} - -// Tests the method DeletePrivilege. -TEST(RestrictedTokenTest, DeletePrivilege) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.DeletePrivilege(SE_CHANGE_NOTIFY_NAME)); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - - ATL::CTokenPrivileges privileges; - ASSERT_TRUE(restricted_token.GetPrivileges(&privileges)); - - ATL::CTokenPrivileges::CNames privilege_names; - ATL::CTokenPrivileges::CAttributes privilege_name_attributes; - privileges.GetNamesAndAttributes(&privilege_names, - &privilege_name_attributes); - - for (unsigned int i = 0; i < privileges.GetCount(); ++i) { - ASSERT_NE(privilege_names[i], SE_CHANGE_NOTIFY_NAME); - } -} - -// Checks if a sid is in the restricting list of the restricted token. -// Asserts if it's not the case. If count is a positive number, the number of -// elements in the restricting sids list has to be equal. -void CheckRestrictingSid(const ATL::CAccessToken &restricted_token, - ATL::CSid sid, int count) { - DWORD length = 1000; - BYTE *memory = new BYTE[1000]; - TOKEN_GROUPS *groups = reinterpret_cast(memory); - ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(), - TokenRestrictedSids, - groups, - length, - &length)); - - ATL::CTokenGroups atl_groups(*groups); - delete[] memory; - - if (count >= 0) - ASSERT_EQ(count, atl_groups.GetCount()); - - ATL::CSid::CSidArray sids; - ATL::CAtlArray attributes; - atl_groups.GetSidsAndAttributes(&sids, &attributes); - - bool present = false; - for (unsigned int i = 0; i < sids.GetCount(); ++i) { - if (sids[i] == sid) { - present = true; - break; - } - } - - ASSERT_TRUE(present); -} - -// Tests the method AddRestrictingSid. -TEST(RestrictedTokenTest, AddRestrictingSid) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, - token.AddRestrictingSid(ATL::Sids::World().GetPSID())); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - - CheckRestrictingSid(restricted_token, ATL::Sids::World(), 1); -} - -// Tests the method AddRestrictingSidCurrentUser. -TEST(RestrictedTokenTest, AddRestrictingSidCurrentUser) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidCurrentUser()); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - ATL::CSid user; - restricted_token.GetUser(&user); - - CheckRestrictingSid(restricted_token, user, 1); -} - -// Tests the method AddRestrictingSidLogonSession. -TEST(RestrictedTokenTest, AddRestrictingSidLogonSession) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidLogonSession()); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - ATL::CSid session; - restricted_token.GetLogonSid(&session); - - CheckRestrictingSid(restricted_token, session, 1); -} - -// Tests adding a lot of restricting sids. -TEST(RestrictedTokenTest, AddMultipleRestrictingSids) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidCurrentUser()); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidLogonSession()); - ASSERT_EQ(ERROR_SUCCESS, - token.AddRestrictingSid(ATL::Sids::World().GetPSID())); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - ATL::CSid session; - restricted_token.GetLogonSid(&session); - - DWORD length = 1000; - BYTE *memory = new BYTE[1000]; - TOKEN_GROUPS *groups = reinterpret_cast(memory); - ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(), - TokenRestrictedSids, - groups, - length, - &length)); - - ATL::CTokenGroups atl_groups(*groups); - delete[] memory; - - ASSERT_EQ(3, atl_groups.GetCount()); -} - -// Tests the method "AddRestrictingSidAllSids". -TEST(RestrictedTokenTest, AddAllSidToRestrictingSids) { - RestrictedToken token; - HANDLE token_handle = NULL; - - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidAllSids()); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); - - ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); - - ATL::CTokenGroups groups; - ASSERT_TRUE(restricted_token.GetGroups(&groups)); - - ATL::CSid::CSidArray sids; - ATL::CAtlArray attributes; - groups.GetSidsAndAttributes(&sids, &attributes); - - // Verify that all group sids are in the restricting sid list. - for (unsigned int i = 0; i < sids.GetCount(); i++) { - if ((attributes[i] & SE_GROUP_INTEGRITY) == 0) { - CheckRestrictingSid(restricted_token, sids[i], -1); - } - } - - // Verify that the user is in the restricting sid list. - ATL::CSid user; - restricted_token.GetUser(&user); - CheckRestrictingSid(restricted_token, user, -1); -} - -// Test to be executed only in release because they are triggering DCHECKs. -#ifndef _DEBUG - -// Checks the error code when the object is initialized twice. -TEST(RestrictedTokenTest, DoubleInit) { - RestrictedToken token; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - - ASSERT_EQ(ERROR_ALREADY_INITIALIZED, token.Init(NULL)); -} - -#endif - -} // namespace sandbox diff --git a/sandbox/src/restricted_token_utils.cc b/sandbox/src/restricted_token_utils.cc deleted file mode 100644 index df30b40..0000000 --- a/sandbox/src/restricted_token_utils.cc +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include - -#include "sandbox/src/restricted_token_utils.h" - -#include "base/logging.h" -#include "base/win/scoped_handle.h" -#include "base/win/scoped_process_information.h" -#include "base/win/windows_version.h" -#include "sandbox/src/job.h" -#include "sandbox/src/restricted_token.h" -#include "sandbox/src/security_level.h" -#include "sandbox/src/sid.h" - -namespace sandbox { - -DWORD CreateRestrictedToken(HANDLE *token_handle, - TokenLevel security_level, - IntegrityLevel integrity_level, - TokenType token_type) { - if (!token_handle) - return ERROR_BAD_ARGUMENTS; - - RestrictedToken restricted_token; - restricted_token.Init(NULL); // Initialized with the current process token - - std::vector privilege_exceptions; - std::vector sid_exceptions; - - bool deny_sids = true; - bool remove_privileges = true; - - switch (security_level) { - case USER_UNPROTECTED: { - deny_sids = false; - remove_privileges = false; - break; - } - case USER_RESTRICTED_SAME_ACCESS: { - deny_sids = false; - remove_privileges = false; - - unsigned err_code = restricted_token.AddRestrictingSidAllSids(); - if (ERROR_SUCCESS != err_code) - return err_code; - - break; - } - case USER_NON_ADMIN: { - sid_exceptions.push_back(WinBuiltinUsersSid); - sid_exceptions.push_back(WinWorldSid); - sid_exceptions.push_back(WinInteractiveSid); - sid_exceptions.push_back(WinAuthenticatedUserSid); - privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); - break; - } - case USER_INTERACTIVE: { - sid_exceptions.push_back(WinBuiltinUsersSid); - sid_exceptions.push_back(WinWorldSid); - sid_exceptions.push_back(WinInteractiveSid); - sid_exceptions.push_back(WinAuthenticatedUserSid); - privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); - restricted_token.AddRestrictingSid(WinBuiltinUsersSid); - restricted_token.AddRestrictingSid(WinWorldSid); - restricted_token.AddRestrictingSid(WinRestrictedCodeSid); - restricted_token.AddRestrictingSidCurrentUser(); - restricted_token.AddRestrictingSidLogonSession(); - break; - } - case USER_LIMITED: { - sid_exceptions.push_back(WinBuiltinUsersSid); - sid_exceptions.push_back(WinWorldSid); - sid_exceptions.push_back(WinInteractiveSid); - privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); - restricted_token.AddRestrictingSid(WinBuiltinUsersSid); - restricted_token.AddRestrictingSid(WinWorldSid); - restricted_token.AddRestrictingSid(WinRestrictedCodeSid); - - // This token has to be able to create objects in BNO. - // Unfortunately, on vista, it needs the current logon sid - // in the token to achieve this. You should also set the process to be - // low integrity level so it can't access object created by other - // processes. - if (base::win::GetVersion() >= base::win::VERSION_VISTA) - restricted_token.AddRestrictingSidLogonSession(); - break; - } - case USER_RESTRICTED: { - privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); - restricted_token.AddUserSidForDenyOnly(); - restricted_token.AddRestrictingSid(WinRestrictedCodeSid); - break; - } - case USER_LOCKDOWN: { - restricted_token.AddUserSidForDenyOnly(); - restricted_token.AddRestrictingSid(WinNullSid); - break; - } - default: { - return ERROR_BAD_ARGUMENTS; - } - } - - DWORD err_code = ERROR_SUCCESS; - if (deny_sids) { - err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions); - if (ERROR_SUCCESS != err_code) - return err_code; - } - - if (remove_privileges) { - err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions); - if (ERROR_SUCCESS != err_code) - return err_code; - } - - restricted_token.SetIntegrityLevel(integrity_level); - - switch (token_type) { - case PRIMARY: { - err_code = restricted_token.GetRestrictedTokenHandle(token_handle); - break; - } - case IMPERSONATION: { - err_code = restricted_token.GetRestrictedTokenHandleForImpersonation( - token_handle); - break; - } - default: { - err_code = ERROR_BAD_ARGUMENTS; - break; - } - } - - return err_code; -} - -DWORD StartRestrictedProcessInJob(wchar_t *command_line, - TokenLevel primary_level, - TokenLevel impersonation_level, - JobLevel job_level, - HANDLE *const job_handle_ret) { - Job job; - DWORD err_code = job.Init(job_level, NULL, 0); - if (ERROR_SUCCESS != err_code) - return err_code; - - if (JOB_UNPROTECTED != job_level) { - // Share the Desktop handle to be able to use MessageBox() in the sandboxed - // application. - err_code = job.UserHandleGrantAccess(GetDesktopWindow()); - if (ERROR_SUCCESS != err_code) - return err_code; - } - - // Create the primary (restricted) token for the process - HANDLE primary_token_handle = NULL; - err_code = CreateRestrictedToken(&primary_token_handle, - primary_level, - INTEGRITY_LEVEL_LAST, - PRIMARY); - if (ERROR_SUCCESS != err_code) { - return err_code; - } - base::win::ScopedHandle primary_token(primary_token_handle); - - // Create the impersonation token (restricted) to be able to start the - // process. - HANDLE impersonation_token_handle; - err_code = CreateRestrictedToken(&impersonation_token_handle, - impersonation_level, - INTEGRITY_LEVEL_LAST, - IMPERSONATION); - if (ERROR_SUCCESS != err_code) { - return err_code; - } - base::win::ScopedHandle impersonation_token(impersonation_token_handle); - - // Start the process - STARTUPINFO startup_info = {0}; - base::win::ScopedProcessInformation process_info; - DWORD flags = CREATE_SUSPENDED; - - if (base::win::GetVersion() < base::win::VERSION_WIN8) { - // Windows 8 implements nested jobs, but for older systems we need to - // break out of any job we're in to enforce our restrictions. - flags |= CREATE_BREAKAWAY_FROM_JOB; - } - - if (!::CreateProcessAsUser(primary_token.Get(), - NULL, // No application name. - command_line, - NULL, // No security attribute. - NULL, // No thread attribute. - FALSE, // Do not inherit handles. - flags, - NULL, // Use the environment of the caller. - NULL, // Use current directory of the caller. - &startup_info, - process_info.Receive())) { - return ::GetLastError(); - } - - // Change the token of the main thread of the new process for the - // impersonation token with more rights. - { - HANDLE temp_thread = process_info.thread_handle(); - if (!::SetThreadToken(&temp_thread, impersonation_token.Get())) { - ::TerminateProcess(process_info.process_handle(), - 0); // exit code - return ::GetLastError(); - } - } - - err_code = job.AssignProcessToJob(process_info.process_handle()); - if (ERROR_SUCCESS != err_code) { - ::TerminateProcess(process_info.process_handle(), - 0); // exit code - return ::GetLastError(); - } - - // Start the application - ::ResumeThread(process_info.thread_handle()); - - (*job_handle_ret) = job.Detach(); - - return ERROR_SUCCESS; -} - -DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type, - const wchar_t* ace_access, - const wchar_t* integrity_level_sid) { - // Build the SDDL string for the label. - std::wstring sddl = L"S:("; // SDDL for a SACL. - sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label". - sddl += L";;"; // No Ace Flags. - sddl += ace_access; // Add the ACE access. - sddl += L";;;"; // No ObjectType and Inherited Object Type. - sddl += integrity_level_sid; // Trustee Sid. - sddl += L")"; - - DWORD error = ERROR_SUCCESS; - PSECURITY_DESCRIPTOR sec_desc = NULL; - - PACL sacl = NULL; - BOOL sacl_present = FALSE; - BOOL sacl_defaulted = FALSE; - - if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(), - SDDL_REVISION, - &sec_desc, NULL)) { - if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl, - &sacl_defaulted)) { - error = ::SetSecurityInfo(handle, type, - LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, - sacl); - } else { - error = ::GetLastError(); - } - - ::LocalFree(sec_desc); - } else { - return::GetLastError(); - } - - return error; -} - -const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) { - switch (integrity_level) { - case INTEGRITY_LEVEL_SYSTEM: - return L"S-1-16-16384"; - case INTEGRITY_LEVEL_HIGH: - return L"S-1-16-12288"; - case INTEGRITY_LEVEL_MEDIUM: - return L"S-1-16-8192"; - case INTEGRITY_LEVEL_MEDIUM_LOW: - return L"S-1-16-6144"; - case INTEGRITY_LEVEL_LOW: - return L"S-1-16-4096"; - case INTEGRITY_LEVEL_BELOW_LOW: - return L"S-1-16-2048"; - case INTEGRITY_LEVEL_UNTRUSTED: - return L"S-1-16-0"; - case INTEGRITY_LEVEL_LAST: - return NULL; - } - - NOTREACHED(); - return NULL; -} -DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) { - if (base::win::GetVersion() < base::win::VERSION_VISTA) - return ERROR_SUCCESS; - - const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level); - if (!integrity_level_str) { - // No mandatory level specified, we don't change it. - return ERROR_SUCCESS; - } - - PSID integrity_sid = NULL; - if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid)) - return ::GetLastError(); - - TOKEN_MANDATORY_LABEL label = {0}; - label.Label.Attributes = SE_GROUP_INTEGRITY; - label.Label.Sid = integrity_sid; - - DWORD size = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(integrity_sid); - BOOL result = ::SetTokenInformation(token, TokenIntegrityLevel, &label, - size); - ::LocalFree(integrity_sid); - - return result ? ERROR_SUCCESS : ::GetLastError(); -} - -DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) { - if (base::win::GetVersion() < base::win::VERSION_VISTA) - return ERROR_SUCCESS; - - // We don't check for an invalid level here because we'll just let it - // fail on the SetTokenIntegrityLevel call later on. - if (integrity_level == INTEGRITY_LEVEL_LAST) { - // No mandatory level specified, we don't change it. - return ERROR_SUCCESS; - } - - HANDLE token_handle; - if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT, - &token_handle)) - return ::GetLastError(); - - base::win::ScopedHandle token(token_handle); - - return SetTokenIntegrityLevel(token.Get(), integrity_level); -} - -} // namespace sandbox diff --git a/sandbox/src/restricted_token_utils.h b/sandbox/src/restricted_token_utils.h deleted file mode 100644 index 0aade8b..0000000 --- a/sandbox/src/restricted_token_utils.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__ -#define SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__ - -#include -#include - -#include "sandbox/src/restricted_token.h" -#include "sandbox/src/security_level.h" - -// Contains the utility functions to be able to create restricted tokens based -// on a security profiles. - -namespace sandbox { - -// The type of the token returned by the CreateNakedToken. -enum TokenType { - IMPERSONATION = 0, - PRIMARY -}; - -// Creates a restricted token based on the effective token of the current -// process. The parameter security_level determines how much the token is -// restricted. The token_type determines if the token will be used as a primary -// token or impersonation token. The integrity level of the token is set to -// |integrity level| on Vista only. -// token_handle is the output value containing the handle of the -// newly created restricted token. -// If the function succeeds, the return value is ERROR_SUCCESS. If the -// function fails, the return value is the win32 error code corresponding to -// the error. -DWORD CreateRestrictedToken(HANDLE *token_handle, - TokenLevel security_level, - IntegrityLevel integrity_level, - TokenType token_type); - -// Starts the process described by the input parameter command_line in a job -// with a restricted token. Also set the main thread of this newly created -// process to impersonate a user with more rights so it can initialize -// correctly. -// -// Parameters: primary_level is the security level of the primary token. -// impersonation_level is the security level of the impersonation token used -// to initialize the process. job_level is the security level of the job -// object used to encapsulate the process. -// -// The output parameter job_handle is the handle to the job object. It has -// to be closed with CloseHandle() when not needed. Closing this handle will -// kill the process started. -// -// Note: The process started with this function has to call RevertToSelf() as -// soon as possible to stop using the impersonation token and start being -// secure. -// -// Note: The Unicode version of this function will fail if the command_line -// parameter is a const string. -DWORD StartRestrictedProcessInJob(wchar_t *command_line, - TokenLevel primary_level, - TokenLevel impersonation_level, - JobLevel job_level, - HANDLE *job_handle); - -// Sets the integrity label on a object handle. -DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type, - const wchar_t* ace_access, - const wchar_t* integrity_level_sid); - -// Sets the integrity level on a token. This is only valid on Vista. It returns -// without failing on XP. If the integrity level that you specify is greater -// than the current integrity level, the function will fail. -DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level); - -// Sets the integrity level on the current process on Vista. It returns without -// failing on XP. If the integrity level that you specify is greater than the -// current integrity level, the function will fail. -DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level); - -} // namespace sandbox - -#endif // SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__ diff --git a/sandbox/src/sandbox.cc b/sandbox/src/sandbox.cc deleted file mode 100644 index f70c702..0000000 --- a/sandbox/src/sandbox.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/broker_services.h" -#include "sandbox/src/target_services.h" - -#if defined(_WIN64) && !defined(NACL_WIN64) -// We allow building this code for Win64 as part of NaCl to enable development -#error Sandbox code was not tested on 64-bit Windows. See \ - http://crbug.com/27218 for details and progress log. -#endif - - -namespace sandbox { -// The section for IPC and policy. -SANDBOX_INTERCEPT HANDLE g_shared_section = NULL; - -static bool s_is_broker = false; - -// GetBrokerServices: the current implementation relies on a shared section -// that is created by the broker and opened by the target. -BrokerServices* SandboxFactory::GetBrokerServices() { - // Can't be the broker if the shared section is open. - if (NULL != g_shared_section) { - return NULL; - } - // If the shared section does not exist we are the broker, then create - // the broker object. - s_is_broker = true; - return BrokerServicesBase::GetInstance(); -} - -// GetTargetServices implementation must follow the same technique as the -// GetBrokerServices, but in this case the logic is the opposite. -TargetServices* SandboxFactory::GetTargetServices() { - // Can't be the target if the section handle is not valid. - if (NULL == g_shared_section) { - return NULL; - } - // We are the target - s_is_broker = false; - // Creates and returns the target services implementation. - return TargetServicesBase::GetInstance(); -} - -} // namespace sandbox diff --git a/sandbox/src/sandbox.h b/sandbox/src/sandbox.h deleted file mode 100644 index 18c05ce..0000000 --- a/sandbox/src/sandbox.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Sandbox is a sandbox library for windows processes. Use when you want a -// 'privileged' process and a 'locked down process' to interact with. -// The privileged process is called the broker and it is started by external -// means (such as the user starting it). The 'sandboxed' process is called the -// target and it is started by the broker. There can be many target processes -// started by a single broker process. This library provides facilities -// for both the broker and the target. -// -// The design rationale and relevant documents can be found at http://go/sbox. -// -// Note: this header does not include the SandboxFactory definitions because -// there are cases where the Sandbox library is linked against the main .exe -// while its API needs to be used in a DLL. - -#ifndef SANDBOX_SRC_SANDBOX_H__ -#define SANDBOX_SRC_SANDBOX_H__ - -#include - -#include "base/basictypes.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/sandbox_types.h" - -// sandbox: Google User-Land Application Sandbox -namespace sandbox { - -class BrokerServices; -class ProcessState; -class TargetPolicy; -class TargetServices; - -// BrokerServices exposes all the broker API. -// The basic use is to start the target(s) and wait for them to end. -// -// This API is intended to be called in the following order -// (error checking omitted): -// BrokerServices* broker = SandboxFactory::GetBrokerServices(); -// broker->Init(); -// PROCESS_INFORMATION target; -// broker->SpawnTarget(target_exe_path, target_args, &target); -// ::ResumeThread(target->hThread); -// // -- later you can call: -// broker->WaitForAllTargets(option); -// -class BrokerServices { - public: - // Initializes the broker. Must be called before any other on this class. - // returns ALL_OK if successful. All other return values imply failure. - // If the return is ERROR_GENERIC, you can call ::GetLastError() to get - // more information. - virtual ResultCode Init() = 0; - - // Returns the interface pointer to a new, empty policy object. Use this - // interface to specify the sandbox policy for new processes created by - // SpawnTarget() - virtual TargetPolicy* CreatePolicy() = 0; - - // Creates a new target (child process) in a suspended state. - // Parameters: - // exe_path: This is the full path to the target binary. This parameter - // can be null and in this case the exe path must be the first argument - // of the command_line. - // command_line: The arguments to be passed as command line to the new - // process. This can be null if the exe_path parameter is not null. - // policy: This is the pointer to the policy object for the sandbox to - // be created. - // target: returns the resulting target process information such as process - // handle and PID just as if CreateProcess() had been called. The caller is - // responsible for closing the handles returned in this structure. - // Returns: - // ALL_OK if successful. All other return values imply failure. - virtual ResultCode SpawnTarget(const wchar_t* exe_path, - const wchar_t* command_line, - TargetPolicy* policy, - PROCESS_INFORMATION* target) = 0; - - // This call blocks (waits) for all the targets to terminate. - // Returns: - // ALL_OK if successful. All other return values imply failure. - // If the return is ERROR_GENERIC, you can call ::GetLastError() to get - // more information. - virtual ResultCode WaitForAllTargets() = 0; - - // Adds an unsandboxed process as a peer for policy decisions (e.g. - // HANDLES_DUP_ANY policy). - // Returns: - // ALL_OK if successful. All other return values imply failure. - // If the return is ERROR_GENERIC, you can call ::GetLastError() to get - // more information. - virtual ResultCode AddTargetPeer(HANDLE peer_process) = 0; -}; - -// TargetServices models the current process from the perspective -// of a target process. To obtain a pointer to it use -// Sandbox::GetTargetServices(). Note that this call returns a non-null -// pointer only if this process is in fact a target. A process is a target -// only if the process was spawned by a call to BrokerServices::SpawnTarget(). -// -// This API allows the target to gain access to resources with a high -// privilege token and then when it is ready to perform dangerous activities -// (such as download content from the web) it can lower its token and -// enter into locked-down (sandbox) mode. -// The typical usage is as follows: -// -// TargetServices* target_services = Sandbox::GetTargetServices(); -// if (NULL != target_services) { -// // We are the target. -// target_services->Init(); -// // Do work that requires high privileges here. -// // .... -// // When ready to enter lock-down mode call LowerToken: -// target_services->LowerToken(); -// } -// -// For more information see the BrokerServices API documentation. -class TargetServices { - public: - // Initializes the target. Must call this function before any other. - // returns ALL_OK if successful. All other return values imply failure. - // If the return is ERROR_GENERIC, you can call ::GetLastError() to get - // more information. - virtual ResultCode Init() = 0; - - // Discards the impersonation token and uses the lower token, call before - // processing any untrusted data or running third-party code. If this call - // fails the current process could be terminated immediately. - virtual void LowerToken() = 0; - - // Returns the ProcessState object. Through that object it's possible to have - // information about the current state of the process, such as whether - // LowerToken has been called or not. - virtual ProcessState* GetState() = 0; - - // Requests the broker to duplicate the supplied handle into the target - // process. The target process must be an active sandbox child process - // and the source process must have a corresponding policy allowing - // handle duplication for this object type. - // Returns: - // ALL_OK if successful. All other return values imply failure. - // If the return is ERROR_GENERIC, you can call ::GetLastError() to get - // more information. - virtual ResultCode DuplicateHandle(HANDLE source_handle, - DWORD target_process_id, - HANDLE* target_handle, - DWORD desired_access, - DWORD options) = 0; -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_SANDBOX_H__ diff --git a/sandbox/src/sandbox.vcproj b/sandbox/src/sandbox.vcproj deleted file mode 100644 index f206e01..0000000 --- a/sandbox/src/sandbox.vcproj +++ /dev/null @@ -1,658 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sandbox/src/sandbox_factory.h b/sandbox/src/sandbox_factory.h deleted file mode 100644 index b52bcb6..0000000 --- a/sandbox/src/sandbox_factory.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SANDBOX_FACTORY_H__ -#define SANDBOX_SRC_SANDBOX_FACTORY_H__ - -#include "sandbox/src/sandbox.h" - -// SandboxFactory is a set of static methods to get access to the broker -// or target services object. Only one of the two methods (GetBrokerServices, -// GetTargetServices) will return a non-null pointer and that should be used -// as the indication that the process is the broker or the target: -// -// BrokerServices* broker_services = SandboxFactory::GetBrokerServices(); -// if (NULL != broker_services) { -// //we are the broker, call broker api here -// broker_services->Init(); -// } else { -// TargetServices* target_services = SandboxFactory::GetTargetServices(); -// if (NULL != target_services) { -// //we are the target, call target api here -// target_services->Init(); -// } -// -// The methods in this class are expected to be called from a single thread -// -// The Sandbox library needs to be linked against the main executable, but -// sometimes the API calls are issued from a DLL that loads into the exe -// process. These factory methods then need to be called from the main -// exe and the interface pointers then can be safely passed to the DLL where -// the Sandbox API calls are made. -namespace sandbox { - -class SandboxFactory { - public: - // Returns the Broker API interface, returns NULL if this process is the - // target. - static BrokerServices* GetBrokerServices(); - - // Returns the Target API interface, returns NULL if this process is the - // broker. - static TargetServices* GetTargetServices(); - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(SandboxFactory); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SANDBOX_FACTORY_H__ diff --git a/sandbox/src/sandbox_nt_types.h b/sandbox/src/sandbox_nt_types.h deleted file mode 100644 index d0d24f8..0000000 --- a/sandbox/src/sandbox_nt_types.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SANDBOX_NT_TYPES_H__ -#define SANDBOX_SRC_SANDBOX_NT_TYPES_H__ - -#include "sandbox/src/nt_internals.h" - -namespace sandbox { - -struct NtExports { - NtAllocateVirtualMemoryFunction AllocateVirtualMemory; - NtCloseFunction Close; - NtDuplicateObjectFunction DuplicateObject; - NtFreeVirtualMemoryFunction FreeVirtualMemory; - NtMapViewOfSectionFunction MapViewOfSection; - NtProtectVirtualMemoryFunction ProtectVirtualMemory; - NtQueryInformationProcessFunction QueryInformationProcess; - NtQueryObjectFunction QueryObject; - NtQuerySectionFunction QuerySection; - NtQueryVirtualMemoryFunction QueryVirtualMemory; - NtUnmapViewOfSectionFunction UnmapViewOfSection; - RtlAllocateHeapFunction RtlAllocateHeap; - RtlAnsiStringToUnicodeStringFunction RtlAnsiStringToUnicodeString; - RtlCompareUnicodeStringFunction RtlCompareUnicodeString; - RtlCreateHeapFunction RtlCreateHeap; - RtlCreateUserThreadFunction RtlCreateUserThread; - RtlDestroyHeapFunction RtlDestroyHeap; - RtlFreeHeapFunction RtlFreeHeap; - _strnicmpFunction _strnicmp; - strlenFunction strlen; - wcslenFunction wcslen; -}; - -// This is the value used for the ntdll level allocator. -enum AllocationType { - NT_ALLOC, - NT_PLACE, - NT_PAGE -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_SANDBOX_NT_TYPES_H__ diff --git a/sandbox/src/sandbox_nt_util.cc b/sandbox/src/sandbox_nt_util.cc deleted file mode 100644 index daa361a..0000000 --- a/sandbox/src/sandbox_nt_util.cc +++ /dev/null @@ -1,599 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/sandbox_nt_util.h" - -#include "base/win/pe_image.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/target_services.h" - -namespace sandbox { - -// This is the list of all imported symbols from ntdll.dll. -SANDBOX_INTERCEPT NtExports g_nt = { NULL }; - -} - -namespace { - -#if defined(_WIN64) -void* AllocateNearTo(void* source, size_t size) { - using sandbox::g_nt; - - // Start with 1 GB above the source. - const unsigned int kOneGB = 0x40000000; - void* base = reinterpret_cast(source) + kOneGB; - SIZE_T actual_size = size; - ULONG_PTR zero_bits = 0; // Not the correct type if used. - ULONG type = MEM_RESERVE; - - if (reinterpret_cast(source) > 0x7ff80000000) { - // We are at the top of the address space. Let's try the highest available - // address. - base = NULL; - type |= MEM_TOP_DOWN; - } - - NTSTATUS ret; - int attempts = 0; - for (; attempts < 20; attempts++) { - ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits, - &actual_size, type, PAGE_READWRITE); - if (NT_SUCCESS(ret)) { - if (base < source) { - // We won't be able to patch this dll. - VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, - MEM_RELEASE)); - return NULL; - } - break; - } - - // Try 100 MB higher. - base = reinterpret_cast(base) + 100 * 0x100000; - }; - - if (attempts == 20) - return NULL; - - ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits, - &actual_size, MEM_COMMIT, PAGE_READWRITE); - - if (!NT_SUCCESS(ret)) { - VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, - MEM_RELEASE)); - base = NULL; - } - - return base; -} -#else // defined(_WIN64). -void* AllocateNearTo(void* source, size_t size) { - using sandbox::g_nt; - UNREFERENCED_PARAMETER(source); - - // In 32-bit processes allocations below 512k are predictable, so mark - // anything in that range as reserved and retry until we get a good address. - const void* const kMinAddress = reinterpret_cast(512 * 1024); - NTSTATUS ret; - SIZE_T actual_size; - void* base; - do { - base = NULL; - actual_size = 64 * 1024; - ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size, - MEM_RESERVE, PAGE_NOACCESS); - if (!NT_SUCCESS(ret)) - return NULL; - } while (base < kMinAddress); - - actual_size = size; - ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size, - MEM_COMMIT, PAGE_READWRITE); - if (!NT_SUCCESS(ret)) - return NULL; - return base; -} -#endif // defined(_WIN64). - -} // namespace. - -namespace sandbox { - -// Handle for our private heap. -void* g_heap = NULL; - -SANDBOX_INTERCEPT HANDLE g_shared_section; -SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0; -SANDBOX_INTERCEPT size_t g_shared_policy_size = 0; - -void* volatile g_shared_policy_memory = NULL; -void* volatile g_shared_IPC_memory = NULL; - -// Both the IPC and the policy share a single region of memory in which the IPC -// memory is first and the policy memory is last. -bool MapGlobalMemory() { - if (NULL == g_shared_IPC_memory) { - void* memory = NULL; - SIZE_T size = 0; - // Map the entire shared section from the start. - NTSTATUS ret = g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess, - &memory, 0, 0, NULL, &size, ViewUnmap, - 0, PAGE_READWRITE); - - if (!NT_SUCCESS(ret) || NULL == memory) { - NOTREACHED_NT(); - return false; - } - - if (NULL != _InterlockedCompareExchangePointer(&g_shared_IPC_memory, - memory, NULL)) { - // Somebody beat us to the memory setup. - ret = g_nt.UnmapViewOfSection(NtCurrentProcess, memory); - VERIFY_SUCCESS(ret); - } - DCHECK_NT(g_shared_IPC_size > 0); - g_shared_policy_memory = reinterpret_cast(g_shared_IPC_memory) - + g_shared_IPC_size; - } - DCHECK_NT(g_shared_policy_memory); - DCHECK_NT(g_shared_policy_size > 0); - return true; -} - -void* GetGlobalIPCMemory() { - if (!MapGlobalMemory()) - return NULL; - return g_shared_IPC_memory; -} - -void* GetGlobalPolicyMemory() { - if (!MapGlobalMemory()) - return NULL; - return g_shared_policy_memory; -} - -bool InitHeap() { - if (!g_heap) { - // Create a new heap using default values for everything. - void* heap = g_nt.RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); - if (!heap) - return false; - - if (NULL != _InterlockedCompareExchangePointer(&g_heap, heap, NULL)) { - // Somebody beat us to the memory setup. - g_nt.RtlDestroyHeap(heap); - } - } - return (g_heap) ? true : false; -} - -// Physically reads or writes from memory to verify that (at this time), it is -// valid. Returns a dummy value. -int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) { - const int kPageSize = 4096; - int dummy = 0; - char* start = reinterpret_cast(buffer); - char* end = start + size_bytes - 1; - - if (WRITE == intent) { - for (; start < end; start += kPageSize) { - *start = 0; - } - *end = 0; - } else { - for (; start < end; start += kPageSize) { - dummy += *start; - } - dummy += *end; - } - - return dummy; -} - -bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) { - DCHECK_NT(size); - __try { - TouchMemory(buffer, size, intent); - } __except(EXCEPTION_EXECUTE_HANDLER) { - return false; - } - return true; -} - -NTSTATUS CopyData(void* destination, const void* source, size_t bytes) { - NTSTATUS ret = STATUS_SUCCESS; - __try { - if (SandboxFactory::GetTargetServices()->GetState()->InitCalled()) { - memcpy(destination, source, bytes); - } else { - const char* from = reinterpret_cast(source); - char* to = reinterpret_cast(destination); - for (size_t i = 0; i < bytes; i++) { - to[i] = from[i]; - } - } - } __except(EXCEPTION_EXECUTE_HANDLER) { - ret = GetExceptionCode(); - } - return ret; -} - -// Hacky code... replace with AllocAndCopyObjectAttributes. -NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object, - wchar_t** out_name, uint32* attributes, - HANDLE* root) { - if (!InitHeap()) - return STATUS_NO_MEMORY; - - DCHECK_NT(out_name); - *out_name = NULL; - NTSTATUS ret = STATUS_UNSUCCESSFUL; - __try { - do { - if (in_object->RootDirectory != static_cast(0) && !root) - break; - if (NULL == in_object->ObjectName) - break; - if (NULL == in_object->ObjectName->Buffer) - break; - - size_t size = in_object->ObjectName->Length + sizeof(wchar_t); - *out_name = new(NT_ALLOC) wchar_t[size/sizeof(wchar_t)]; - if (NULL == *out_name) - break; - - ret = CopyData(*out_name, in_object->ObjectName->Buffer, - size - sizeof(wchar_t)); - if (!NT_SUCCESS(ret)) - break; - - (*out_name)[size / sizeof(wchar_t) - 1] = L'\0'; - - if (attributes) - *attributes = in_object->Attributes; - - if (root) - *root = in_object->RootDirectory; - ret = STATUS_SUCCESS; - } while (false); - } __except(EXCEPTION_EXECUTE_HANDLER) { - ret = GetExceptionCode(); - } - - if (!NT_SUCCESS(ret) && *out_name) { - operator delete(*out_name, NT_ALLOC); - *out_name = NULL; - } - - return ret; -} - -NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) { - PROCESS_BASIC_INFORMATION proc_info; - ULONG bytes_returned; - - NTSTATUS ret = g_nt.QueryInformationProcess(process, ProcessBasicInformation, - &proc_info, sizeof(proc_info), - &bytes_returned); - if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned) - return ret; - - *process_id = proc_info.UniqueProcessId; - return STATUS_SUCCESS; -} - -bool IsSameProcess(HANDLE process) { - if (NtCurrentProcess == process) - return true; - - static ULONG s_process_id = 0; - - if (!s_process_id) { - NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id); - if (!NT_SUCCESS(ret)) - return false; - } - - ULONG process_id; - NTSTATUS ret = GetProcessId(process, &process_id); - if (!NT_SUCCESS(ret)) - return false; - - return (process_id == s_process_id); -} - -bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset, - PSIZE_T view_size) { - if (!section || !base || !view_size || offset) - return false; - - HANDLE query_section; - - NTSTATUS ret = g_nt.DuplicateObject(NtCurrentProcess, section, - NtCurrentProcess, &query_section, - SECTION_QUERY, 0, 0); - if (!NT_SUCCESS(ret)) - return false; - - SECTION_BASIC_INFORMATION basic_info; - SIZE_T bytes_returned; - ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info, - sizeof(basic_info), &bytes_returned); - - VERIFY_SUCCESS(g_nt.Close(query_section)); - - if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned) - return false; - - if (!(basic_info.Attributes & SEC_IMAGE)) - return false; - - return true; -} - -UNICODE_STRING* AnsiToUnicode(const char* string) { - ANSI_STRING ansi_string; - ansi_string.Length = static_cast(g_nt.strlen(string)); - ansi_string.MaximumLength = ansi_string.Length + 1; - ansi_string.Buffer = const_cast(string); - - if (ansi_string.Length > ansi_string.MaximumLength) - return NULL; - - size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t) + - sizeof(UNICODE_STRING); - - UNICODE_STRING* out_string = reinterpret_cast( - new(NT_ALLOC) char[name_bytes]); - if (!out_string) - return NULL; - - out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t); - out_string->Buffer = reinterpret_cast(&out_string[1]); - - BOOLEAN alloc_destination = FALSE; - NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string, - alloc_destination); - DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret); - if (!NT_SUCCESS(ret)) { - operator delete(out_string, NT_ALLOC); - return NULL; - } - - return out_string; -} - -UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) { - UNICODE_STRING* out_name = NULL; - __try { - do { - *flags = 0; - base::win::PEImage pe(module); - - if (!pe.VerifyMagic()) - break; - *flags |= MODULE_IS_PE_IMAGE; - - PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory(); - if (exports) { - char* name = reinterpret_cast(pe.RVAToAddr(exports->Name)); - out_name = AnsiToUnicode(name); - } - - PIMAGE_NT_HEADERS headers = pe.GetNTHeaders(); - if (headers) { - if (headers->OptionalHeader.AddressOfEntryPoint) - *flags |= MODULE_HAS_ENTRY_POINT; - if (headers->OptionalHeader.SizeOfCode) - *flags |= MODULE_HAS_CODE; - } - } while (false); - } __except(EXCEPTION_EXECUTE_HANDLER) { - } - - return out_name; -} - -UNICODE_STRING* GetBackingFilePath(PVOID address) { - // We'll start with something close to max_path charactes for the name. - ULONG buffer_bytes = MAX_PATH * 2; - - for (;;) { - MEMORY_SECTION_NAME* section_name = reinterpret_cast( - new(NT_ALLOC) char[buffer_bytes]); - - if (!section_name) - return NULL; - - ULONG returned_bytes; - NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address, - MemorySectionName, section_name, - buffer_bytes, &returned_bytes); - - if (STATUS_BUFFER_OVERFLOW == ret) { - // Retry the call with the given buffer size. - operator delete(section_name, NT_ALLOC); - section_name = NULL; - buffer_bytes = returned_bytes; - continue; - } - if (!NT_SUCCESS(ret)) { - operator delete(section_name, NT_ALLOC); - return NULL; - } - - return reinterpret_cast(section_name); - } -} - -UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) { - if ((!module_path) || (!module_path->Buffer)) - return NULL; - - wchar_t* sep = NULL; - int start_pos = module_path->Length / sizeof(wchar_t) - 1; - int ix = start_pos; - - for (; ix >= 0; --ix) { - if (module_path->Buffer[ix] == L'\\') { - sep = &module_path->Buffer[ix]; - break; - } - } - - // Ends with path separator. Not a valid module name. - if ((ix == start_pos) && sep) - return NULL; - - // No path separator found. Use the entire name. - if (!sep) { - sep = &module_path->Buffer[-1]; - } - - // Add one to the size so we can null terminate the string. - size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t); - - // Based on the code above, size_bytes should always be small enough - // to make the static_cast below safe. - DCHECK_NT(kuint16max > size_bytes); - char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)]; - if (!str_buffer) - return NULL; - - UNICODE_STRING* out_string = reinterpret_cast(str_buffer); - out_string->Buffer = reinterpret_cast(&out_string[1]); - out_string->Length = static_cast(size_bytes - sizeof(wchar_t)); - out_string->MaximumLength = static_cast(size_bytes); - - NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length); - if (!NT_SUCCESS(ret)) { - operator delete(out_string, NT_ALLOC); - return NULL; - } - - out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0'; - return out_string; -} - -NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes, - ULONG protect) { - DCHECK_NT(!changed_); - SIZE_T new_bytes = bytes; - NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address, - &new_bytes, protect, &old_protect_); - if (NT_SUCCESS(ret)) { - changed_ = true; - address_ = address; - bytes_ = new_bytes; - } - - return ret; -} - -NTSTATUS AutoProtectMemory::RevertProtection() { - if (!changed_) - return STATUS_SUCCESS; - - DCHECK_NT(address_); - DCHECK_NT(bytes_); - - SIZE_T new_bytes = bytes_; - NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address_, - &new_bytes, old_protect_, - &old_protect_); - DCHECK_NT(NT_SUCCESS(ret)); - - changed_ = false; - address_ = NULL; - bytes_ = 0; - old_protect_ = 0; - - return ret; -} - -bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, - uint32 file_info_class) { - if (FileRenameInformation != file_info_class) - return false; - - if (length < sizeof(FILE_RENAME_INFORMATION)) - return false; - - // Make sure file name length doesn't exceed the message length - if (length - offsetof(FILE_RENAME_INFORMATION, FileName) < - file_info->FileNameLength) - return false; - - // We don't support a root directory. - if (file_info->RootDirectory) - return false; - - // Check if it starts with \\??\\. We don't support relative paths. - if (file_info->FileNameLength < 4 || file_info->FileNameLength > kuint16max) - return false; - - if (file_info->FileName[0] != L'\\' || - file_info->FileName[1] != L'?' || - file_info->FileName[2] != L'?' || - file_info->FileName[3] != L'\\') - return false; - - return true; -} - -} // namespace sandbox - -void* operator new(size_t size, sandbox::AllocationType type, - void* near_to) { - using namespace sandbox; - - if (NT_ALLOC == type) { - if (!InitHeap()) - return NULL; - - // Use default flags for the allocation. - return g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size); - } else if (NT_PAGE == type) { - return AllocateNearTo(near_to, size); - } - NOTREACHED_NT(); - return NULL; -} - -void operator delete(void* memory, sandbox::AllocationType type) { - using namespace sandbox; - - if (NT_ALLOC == type) { - // Use default flags. - VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory)); - } else if (NT_PAGE == type) { - void* base = memory; - SIZE_T size = 0; - VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, - MEM_RELEASE)); - } else { - NOTREACHED_NT(); - } -} - -void operator delete(void* memory, sandbox::AllocationType type, - void* near_to) { - UNREFERENCED_PARAMETER(near_to); - operator delete(memory, type); -} - -void* __cdecl operator new(size_t size, void* buffer, - sandbox::AllocationType type) { - UNREFERENCED_PARAMETER(size); - UNREFERENCED_PARAMETER(type); - return buffer; -} - -void __cdecl operator delete(void* memory, void* buffer, - sandbox::AllocationType type) { - UNREFERENCED_PARAMETER(memory); - UNREFERENCED_PARAMETER(buffer); - UNREFERENCED_PARAMETER(type); -} diff --git a/sandbox/src/sandbox_nt_util.h b/sandbox/src/sandbox_nt_util.h deleted file mode 100644 index 7a82302..0000000 --- a/sandbox/src/sandbox_nt_util.h +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SANDBOX_NT_UTIL_H_ -#define SANDBOX_SRC_SANDBOX_NT_UTIL_H_ - -#include "base/basictypes.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_nt_types.h" - -// Placement new and delete to be used from ntdll interception code. -void* __cdecl operator new(size_t size, sandbox::AllocationType type, - void* near_to = NULL); -void __cdecl operator delete(void* memory, sandbox::AllocationType type); -// Add operator delete that matches the placement form of the operator new -// above. This is required by compiler to generate code to call operator delete -// in case the object's constructor throws an exception. -// See http://msdn.microsoft.com/en-us/library/cxdxz3x6.aspx -void __cdecl operator delete(void* memory, sandbox::AllocationType type, - void* near_to); - -// Regular placement new and delete -void* __cdecl operator new(size_t size, void* buffer, - sandbox::AllocationType type); -void __cdecl operator delete(void* memory, void* buffer, - sandbox::AllocationType type); - -// DCHECK_NT is defined to be pretty much an assert at this time because we -// don't have logging from the ntdll layer on the child. -// -// VERIFY_NT and VERIFY_SUCCESS_NT are the standard asserts on debug, but -// execute the actual argument on release builds. VERIFY_NT expects an action -// returning a bool, while VERIFY_SUCCESS_NT expects an action returning -// NTSTATUS. -#ifndef NDEBUG -#define DCHECK_NT(condition) { (condition) ? 0 : __debugbreak(); } -#define VERIFY(action) DCHECK_NT(action) -#define VERIFY_SUCCESS(action) DCHECK_NT(NT_SUCCESS(action)) -#else -#define DCHECK_NT(condition) -#define VERIFY(action) (action) -#define VERIFY_SUCCESS(action) (action) -#endif - -#define NOTREACHED_NT() DCHECK_NT(false) - -namespace sandbox { - -extern "C" long _InterlockedCompareExchange(long volatile* destination, - long exchange, long comperand); - -#pragma intrinsic(_InterlockedCompareExchange) - -// We want to make sure that we use an intrinsic version of the function, not -// the one provided by kernel32. -__forceinline void* _InterlockedCompareExchangePointer( - void* volatile* destination, void* exchange, void* comperand) { - size_t ret = _InterlockedCompareExchange( - reinterpret_cast(destination), - static_cast(reinterpret_cast(exchange)), - static_cast(reinterpret_cast(comperand))); - - return reinterpret_cast(static_cast(ret)); -} - -// Returns a pointer to the IPC shared memory. -void* GetGlobalIPCMemory(); - -// Returns a pointer to the Policy shared memory. -void* GetGlobalPolicyMemory(); - -enum RequiredAccess { - READ, - WRITE -}; - -// Performs basic user mode buffer validation. In any case, buffers access must -// be protected by SEH. intent specifies if the buffer should be tested for read -// or write. -// Note that write intent implies destruction of the buffer content (we actually -// write) -bool ValidParameter(void* buffer, size_t size, RequiredAccess intent); - - -// Copies data from a user buffer to our buffer. Returns the operation status. -NTSTATUS CopyData(void* destination, const void* source, size_t bytes); - -// Copies the name from an object attributes. -NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object, - wchar_t** out_name, uint32* attributes, HANDLE* root); - -// Initializes our ntdll level heap -bool InitHeap(); - -// Returns true if the provided handle refers to the current process. -bool IsSameProcess(HANDLE process); - -enum MappedModuleFlags { - MODULE_IS_PE_IMAGE = 1, // Module is an executable. - MODULE_HAS_ENTRY_POINT = 2, // Execution entry point found. - MODULE_HAS_CODE = 4 // Non zero size of executable sections. -}; - -// Returns the name and characteristics for a given PE module. The return -// value is the name as defined by the export table and the flags is any -// combination of the MappedModuleFlags enumeration. -// -// The returned buffer must be freed with a placement delete from the ntdll -// level allocator: -// -// UNICODE_STRING* name = GetPEImageInfoFromModule(HMODULE module, &flags); -// if (!name) { -// // probably not a valid dll -// return; -// } -// InsertYourLogicHere(name); -// operator delete(name, NT_ALLOC); -UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags); - -// Returns the full path and filename for a given dll. -// May return NULL if the provided address is not backed by a named section, or -// if the current OS version doesn't support the call. The returned buffer must -// be freed with a placement delete (see GetImageNameFromModule example). -UNICODE_STRING* GetBackingFilePath(PVOID address); - -// Returns the last component of a path that contains the module name. -// It will return NULL if the path ends with the path separator. The returned -// buffer must be freed with a placement delete (see GetImageNameFromModule -// example). -UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path); - -// Returns true if the parameters correspond to a dll mapped as code. -bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset, - PSIZE_T view_size); - -// Converts an ansi string to an UNICODE_STRING. -UNICODE_STRING* AnsiToUnicode(const char* string); - -// Provides a simple way to temporarily change the protection of a memory page. -class AutoProtectMemory { - public: - AutoProtectMemory() - : changed_(false), address_(NULL), bytes_(0), old_protect_(0) {} - - ~AutoProtectMemory() { - RevertProtection(); - } - - // Sets the desired protection of a given memory range. - NTSTATUS ChangeProtection(void* address, size_t bytes, ULONG protect); - - // Restores the original page protection. - NTSTATUS RevertProtection(); - - private: - bool changed_; - void* address_; - size_t bytes_; - ULONG old_protect_; - - DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory); -}; - -// Returns true if the file_rename_information structure is supported by our -// rename handler. -bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, - uint32 file_info_class); - -} // namespace sandbox - - -#endif // SANDBOX_SRC_SANDBOX_NT_UTIL_H__ diff --git a/sandbox/src/sandbox_policy.h b/sandbox/src/sandbox_policy.h deleted file mode 100644 index 288adc45..0000000 --- a/sandbox/src/sandbox_policy.h +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SANDBOX_POLICY_H_ -#define SANDBOX_SRC_SANDBOX_POLICY_H_ - -#include - -#include "base/basictypes.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/security_level.h" - -namespace sandbox { - -class TargetPolicy { - public: - // Windows subsystems that can have specific rules. - // Note: The process subsystem(SUBSY_PROCESS) does not evaluate the request - // exactly like the CreateProcess API does. See the comment at the top of - // process_thread_dispatcher.cc for more details. - enum SubSystem { - SUBSYS_FILES, // Creation and opening of files and pipes. - SUBSYS_NAMED_PIPES, // Creation of named pipes. - SUBSYS_PROCESS, // Creation of child processes. - SUBSYS_REGISTRY, // Creation and opening of registry keys. - SUBSYS_SYNC, // Creation of named sync objects. - SUBSYS_HANDLES // Duplication of handles to other processes. - }; - - // Allowable semantics when a rule is matched. - enum Semantics { - FILES_ALLOW_ANY, // Allows open or create for any kind of access that - // the file system supports. - FILES_ALLOW_READONLY, // Allows open or create with read access only. - FILES_ALLOW_QUERY, // Allows access to query the attributes of a file. - FILES_ALLOW_DIR_ANY, // Allows open or create with directory semantics - // only. - HANDLES_DUP_ANY, // Allows duplicating handles opened with any - // access permissions. - HANDLES_DUP_BROKER, // Allows duplicating handles to the broker process. - NAMEDPIPES_ALLOW_ANY, // Allows creation of a named pipe. - PROCESS_MIN_EXEC, // Allows to create a process with minimal rights - // over the resulting process and thread handles. - // No other parameters besides the command line are - // passed to the child process. - PROCESS_ALL_EXEC, // Allows the creation of a process and return fill - // access on the returned handles. - // This flag can be used only when the main token of - // the sandboxed application is at least INTERACTIVE. - EVENTS_ALLOW_ANY, // Allows the creation of an event with full access. - EVENTS_ALLOW_READONLY, // Allows opening an even with synchronize access. - REG_ALLOW_READONLY, // Allows readonly access to a registry key. - REG_ALLOW_ANY // Allows read and write access to a registry key. - }; - - // Increments the reference count of this object. The reference count must - // be incremented if this interface is given to another component. - virtual void AddRef() = 0; - - // Decrements the reference count of this object. When the reference count - // is zero the object is automatically destroyed. - // Indicates that the caller is done with this interface. After calling - // release no other method should be called. - virtual void Release() = 0; - - // Sets the security level for the target process' two tokens. - // This setting is permanent and cannot be changed once the target process is - // spawned. - // initial: the security level for the initial token. This is the token that - // is used by the process from the creation of the process until the moment - // the process calls TargetServices::LowerToken() or the process calls - // win32's ReverToSelf(). Once this happens the initial token is no longer - // available and the lockdown token is in effect. - // lockdown: the security level for the token that comes into force after the - // process calls TargetServices::LowerToken() or the process calls - // ReverToSelf(). See the explanation of each level in the TokenLevel - // definition. - // Return value: SBOX_ALL_OK if the setting succeeds and false otherwise. - // Returns false if the lockdown value is more permissive than the initial - // value. - // - // Important: most of the sandbox-provided security relies on this single - // setting. The caller should strive to set the lockdown level as restricted - // as possible. - virtual ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) = 0; - - // Sets the security level of the Job Object to which the target process will - // belong. This setting is permanent and cannot be changed once the target - // process is spawned. The job controls the global security settings which - // can not be specified in the token security profile. - // job_level: the security level for the job. See the explanation of each - // level in the JobLevel definition. - // ui_exceptions: specify what specific rights that are disabled in the - // chosen job_level that need to be granted. Use this parameter to avoid - // selecting the next permissive job level unless you need all the rights - // that are granted in such level. - // The exceptions can be specified as a combination of the following - // constants: - // JOB_OBJECT_UILIMIT_HANDLES : grant access to all user-mode handles. These - // include windows, icons, menus and various GDI objects. In addition the - // target process can set hooks, and broadcast messages to other processes - // that belong to the same desktop. - // JOB_OBJECT_UILIMIT_READCLIPBOARD : grant read-only access to the clipboard. - // JOB_OBJECT_UILIMIT_WRITECLIPBOARD : grant write access to the clipboard. - // JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS : allow changes to the system-wide - // parameters as defined by the Win32 call SystemParametersInfo(). - // JOB_OBJECT_UILIMIT_DISPLAYSETTINGS : allow programmatic changes to the - // display settings. - // JOB_OBJECT_UILIMIT_GLOBALATOMS : allow access to the global atoms table. - // JOB_OBJECT_UILIMIT_DESKTOP : allow the creation of new desktops. - // JOB_OBJECT_UILIMIT_EXITWINDOWS : allow the call to ExitWindows(). - // - // Return value: SBOX_ALL_OK if the setting succeeds and false otherwise. - // - // Note: JOB_OBJECT_XXXX constants are defined in winnt.h and documented at - // length in: - // http://msdn2.microsoft.com/en-us/library/ms684152.aspx - // - // Note: the recommended level is JOB_RESTRICTED or JOB_LOCKDOWN. - virtual ResultCode SetJobLevel(JobLevel job_level, uint32 ui_exceptions) = 0; - - // Specifies the desktop on which the application is going to run. If the - // desktop does not exist, it will be created. If alternate_winstation is - // set to true, the desktop will be created on an alternate window station. - virtual ResultCode SetAlternateDesktop(bool alternate_winstation) = 0; - - // Returns the name of the alternate desktop used. If an alternate window - // station is specified, the name is prepended by the window station name, - // followed by a backslash. - virtual std::wstring GetAlternateDesktop() const = 0; - - // Precreates the desktop and window station, if any. - virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) = 0; - - // Destroys the desktop and windows station. - virtual void DestroyAlternateDesktop() = 0; - - // Sets the integrity level of the process in the sandbox. Both the initial - // token and the main token will be affected by this. This is valid only - // on Vista. It is silently ignored on other OSes. If you set the integrity - // level to a level higher than your current level, the sandbox will fail - // to start. - virtual ResultCode SetIntegrityLevel(IntegrityLevel level) = 0; - - // Sets the integrity level of the process in the sandbox. The integrity level - // will not take effect before you call LowerToken. User Interface Privilege - // Isolation is not affected by this setting and will remain off for the - // process in the sandbox. This flag is valid on Vista only, it is silently - // ignored on other OSes. If you set the integrity level to a level higher - // than your current level, the sandbox will fail to start. - virtual ResultCode SetDelayedIntegrityLevel(IntegrityLevel level) = 0; - - // Sets the interceptions to operate in strict mode. By default, interceptions - // are performed in "relaxed" mode, where if something inside NTDLL.DLL is - // already patched we attempt to intercept it anyway. Setting interceptions - // to strict mode means that when we detect that the function is patched we'll - // refuse to perform the interception. - virtual void SetStrictInterceptions() = 0; - - // Adds a policy rule effective for processes spawned using this policy. - // subsystem: One of the above enumerated windows subsystems. - // semantics: One of the above enumerated FileSemantics. - // pattern: A specific full path or a full path with wildcard patterns. - // The valid wildcards are: - // '*' : Matches zero or more character. Only one in series allowed. - // '?' : Matches a single character. One or more in series are allowed. - // Examples: - // "c:\\documents and settings\\vince\\*.dmp" - // "c:\\documents and settings\\*\\crashdumps\\*.dmp" - // "c:\\temp\\app_log_?????_chrome.txt" - virtual ResultCode AddRule(SubSystem subsystem, Semantics semantics, - const wchar_t* pattern) = 0; - - // Adds a dll that will be unloaded in the target process before it gets - // a chance to initialize itself. Typically, dlls that cause the target - // to crash go here. - virtual ResultCode AddDllToUnload(const wchar_t* dll_name) = 0; - - // Adds a handle that will be closed in the target process after lockdown. - // A NULL value for handle_name indicates all handles of the specified type. - // An empty string for handle_name indicates the handle is unnamed. - virtual ResultCode AddKernelObjectToClose(const wchar_t* handle_type, - const wchar_t* handle_name) = 0; -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_SANDBOX_POLICY_H_ diff --git a/sandbox/src/sandbox_policy_base.cc b/sandbox/src/sandbox_policy_base.cc deleted file mode 100644 index 4438641..0000000 --- a/sandbox/src/sandbox_policy_base.cc +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/sandbox_policy_base.h" - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/logging.h" -#include "sandbox/src/filesystem_dispatcher.h" -#include "sandbox/src/filesystem_policy.h" -#include "sandbox/src/handle_dispatcher.h" -#include "sandbox/src/handle_policy.h" -#include "sandbox/src/job.h" -#include "sandbox/src/interception.h" -#include "sandbox/src/named_pipe_dispatcher.h" -#include "sandbox/src/named_pipe_policy.h" -#include "sandbox/src/policy_broker.h" -#include "sandbox/src/policy_engine_processor.h" -#include "sandbox/src/policy_low_level.h" -#include "sandbox/src/process_thread_dispatcher.h" -#include "sandbox/src/process_thread_policy.h" -#include "sandbox/src/registry_dispatcher.h" -#include "sandbox/src/registry_policy.h" -#include "sandbox/src/restricted_token_utils.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/sync_dispatcher.h" -#include "sandbox/src/sync_policy.h" -#include "sandbox/src/target_process.h" -#include "sandbox/src/window.h" - -namespace { -// The standard windows size for one memory page. -const size_t kOneMemPage = 4096; -// The IPC and Policy shared memory sizes. -const size_t kIPCMemSize = kOneMemPage * 2; -const size_t kPolMemSize = kOneMemPage * 14; - -// Helper function to allocate space (on the heap) for policy. -sandbox::PolicyGlobal* MakeBrokerPolicyMemory() { - const size_t kTotalPolicySz = kPolMemSize; - char* mem = new char[kTotalPolicySz]; - DCHECK(mem); - memset(mem, 0, kTotalPolicySz); - sandbox::PolicyGlobal* policy = reinterpret_cast(mem); - policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal); - return policy; -} -} - -namespace sandbox { - -SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level; - -// Initializes static members. -HWINSTA PolicyBase::alternate_winstation_handle_ = NULL; -HDESK PolicyBase::alternate_desktop_handle_ = NULL; - -PolicyBase::PolicyBase() - : ref_count(1), - lockdown_level_(USER_LOCKDOWN), - initial_level_(USER_LOCKDOWN), - job_level_(JOB_LOCKDOWN), - ui_exceptions_(0), - use_alternate_desktop_(false), - use_alternate_winstation_(false), - file_system_init_(false), - relaxed_interceptions_(true), - integrity_level_(INTEGRITY_LEVEL_LAST), - delayed_integrity_level_(INTEGRITY_LEVEL_LAST), - policy_maker_(NULL), - policy_(NULL) { - ::InitializeCriticalSection(&lock_); - // Initialize the IPC dispatcher array. - memset(&ipc_targets_, NULL, sizeof(ipc_targets_)); - Dispatcher* dispatcher = NULL; - - dispatcher = new FilesystemDispatcher(this); - ipc_targets_[IPC_NTCREATEFILE_TAG] = dispatcher; - ipc_targets_[IPC_NTOPENFILE_TAG] = dispatcher; - ipc_targets_[IPC_NTSETINFO_RENAME_TAG] = dispatcher; - ipc_targets_[IPC_NTQUERYATTRIBUTESFILE_TAG] = dispatcher; - ipc_targets_[IPC_NTQUERYFULLATTRIBUTESFILE_TAG] = dispatcher; - - dispatcher = new NamedPipeDispatcher(this); - ipc_targets_[IPC_CREATENAMEDPIPEW_TAG] = dispatcher; - - dispatcher = new ThreadProcessDispatcher(this); - ipc_targets_[IPC_NTOPENTHREAD_TAG] = dispatcher; - ipc_targets_[IPC_NTOPENPROCESS_TAG] = dispatcher; - ipc_targets_[IPC_CREATEPROCESSW_TAG] = dispatcher; - ipc_targets_[IPC_NTOPENPROCESSTOKEN_TAG] = dispatcher; - ipc_targets_[IPC_NTOPENPROCESSTOKENEX_TAG] = dispatcher; - - dispatcher = new SyncDispatcher(this); - ipc_targets_[IPC_CREATEEVENT_TAG] = dispatcher; - ipc_targets_[IPC_OPENEVENT_TAG] = dispatcher; - - dispatcher = new RegistryDispatcher(this); - ipc_targets_[IPC_NTCREATEKEY_TAG] = dispatcher; - ipc_targets_[IPC_NTOPENKEY_TAG] = dispatcher; - - dispatcher = new HandleDispatcher(this); - ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG] = dispatcher; -} - -PolicyBase::~PolicyBase() { - TargetSet::iterator it; - for (it = targets_.begin(); it != targets_.end(); ++it) { - TargetProcess* target = (*it); - delete target; - } - delete ipc_targets_[IPC_NTCREATEFILE_TAG]; - delete ipc_targets_[IPC_CREATENAMEDPIPEW_TAG]; - delete ipc_targets_[IPC_NTOPENTHREAD_TAG]; - delete ipc_targets_[IPC_CREATEEVENT_TAG]; - delete ipc_targets_[IPC_NTCREATEKEY_TAG]; - delete ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG]; - delete policy_maker_; - delete policy_; - ::DeleteCriticalSection(&lock_); -} - -void PolicyBase::AddRef() { - ::InterlockedIncrement(&ref_count); -} - -void PolicyBase::Release() { - if (0 == ::InterlockedDecrement(&ref_count)) - delete this; -} - -ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) { - if (initial < lockdown) { - return SBOX_ERROR_BAD_PARAMS; - } - initial_level_ = initial; - lockdown_level_ = lockdown; - return SBOX_ALL_OK; -} - -ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) { - job_level_ = job_level; - ui_exceptions_ = ui_exceptions; - return SBOX_ALL_OK; -} - -ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) { - use_alternate_desktop_ = true; - use_alternate_winstation_ = alternate_winstation; - return CreateAlternateDesktop(alternate_winstation); -} - -std::wstring PolicyBase::GetAlternateDesktop() const { - // No alternate desktop or winstation. Return an empty string. - if (!use_alternate_desktop_ && !use_alternate_winstation_) { - return std::wstring(); - } - - // The desktop and winstation should have been created by now. - // If we hit this scenario, it means that the user ignored the failure - // during SetAlternateDesktop, so we ignore it here too. - if (use_alternate_desktop_ && !alternate_desktop_handle_) { - return std::wstring(); - } - if (use_alternate_winstation_ && (!alternate_desktop_handle_ || - !alternate_winstation_handle_)) { - return std::wstring(); - } - - return GetFullDesktopName(alternate_winstation_handle_, - alternate_desktop_handle_); -} - -ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) { - if (alternate_winstation) { - // Previously called with alternate_winstation = false? - if (!alternate_winstation_handle_ && alternate_desktop_handle_) - return SBOX_ERROR_UNSUPPORTED; - - // Check if it's already created. - if (alternate_winstation_handle_ && alternate_desktop_handle_) - return SBOX_ALL_OK; - - DCHECK(!alternate_winstation_handle_); - // Create the window station. - ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_); - if (SBOX_ALL_OK != result) - return result; - - // Verify that everything is fine. - if (!alternate_winstation_handle_ || - GetWindowObjectName(alternate_winstation_handle_).empty()) - return SBOX_ERROR_CANNOT_CREATE_DESKTOP; - - // Create the destkop. - result = CreateAltDesktop(alternate_winstation_handle_, - &alternate_desktop_handle_); - if (SBOX_ALL_OK != result) - return result; - - // Verify that everything is fine. - if (!alternate_desktop_handle_ || - GetWindowObjectName(alternate_desktop_handle_).empty()) - return SBOX_ERROR_CANNOT_CREATE_DESKTOP; - } else { - // Previously called with alternate_winstation = true? - if (alternate_winstation_handle_) - return SBOX_ERROR_UNSUPPORTED; - - // Check if it already exists. - if (alternate_desktop_handle_) - return SBOX_ALL_OK; - - // Create the destkop. - ResultCode result = CreateAltDesktop(NULL, &alternate_desktop_handle_); - if (SBOX_ALL_OK != result) - return result; - - // Verify that everything is fine. - if (!alternate_desktop_handle_ || - GetWindowObjectName(alternate_desktop_handle_).empty()) - return SBOX_ERROR_CANNOT_CREATE_DESKTOP; - } - - return SBOX_ALL_OK; -} - -void PolicyBase::DestroyAlternateDesktop() { - if (alternate_desktop_handle_) { - ::CloseDesktop(alternate_desktop_handle_); - alternate_desktop_handle_ = NULL; - } - - if (alternate_winstation_handle_) { - ::CloseWindowStation(alternate_winstation_handle_); - alternate_winstation_handle_ = NULL; - } -} - -ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) { - integrity_level_ = integrity_level; - return SBOX_ALL_OK; -} - -ResultCode PolicyBase::SetDelayedIntegrityLevel( - IntegrityLevel integrity_level) { - delayed_integrity_level_ = integrity_level; - return SBOX_ALL_OK; -} - -void PolicyBase::SetStrictInterceptions() { - relaxed_interceptions_ = false; -} - -ResultCode PolicyBase::AddRule(SubSystem subsystem, Semantics semantics, - const wchar_t* pattern) { - if (NULL == policy_) { - policy_ = MakeBrokerPolicyMemory(); - DCHECK(policy_); - policy_maker_ = new LowLevelPolicy(policy_); - DCHECK(policy_maker_); - } - - switch (subsystem) { - case SUBSYS_FILES: { - if (!file_system_init_) { - if (!FileSystemPolicy::SetInitialRules(policy_maker_)) - return SBOX_ERROR_BAD_PARAMS; - file_system_init_ = true; - } - if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) { - NOTREACHED(); - return SBOX_ERROR_BAD_PARAMS; - } - break; - } - case SUBSYS_SYNC: { - if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) { - NOTREACHED(); - return SBOX_ERROR_BAD_PARAMS; - } - break; - } - case SUBSYS_PROCESS: { - if (lockdown_level_ < USER_INTERACTIVE && - TargetPolicy::PROCESS_ALL_EXEC == semantics) { - // This is unsupported. This is a huge security risk to give full access - // to a process handle. - return SBOX_ERROR_UNSUPPORTED; - } - if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) { - NOTREACHED(); - return SBOX_ERROR_BAD_PARAMS; - } - break; - } - case SUBSYS_NAMED_PIPES: { - if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) { - NOTREACHED(); - return SBOX_ERROR_BAD_PARAMS; - } - break; - } - case SUBSYS_REGISTRY: { - if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) { - NOTREACHED(); - return SBOX_ERROR_BAD_PARAMS; - } - break; - } - case SUBSYS_HANDLES: { - if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) { - NOTREACHED(); - return SBOX_ERROR_BAD_PARAMS; - } - break; - } - default: { - return SBOX_ERROR_UNSUPPORTED; - } - } - - return SBOX_ALL_OK; -} - -ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) { - blacklisted_dlls_.push_back(std::wstring(dll_name)); - return SBOX_ALL_OK; -} - -ResultCode PolicyBase::AddKernelObjectToClose(const char16* handle_type, - const char16* handle_name) { - return handle_closer_.AddHandle(handle_type, handle_name); -} - -// When an IPC is ready in any of the targets we get called. We manage an array -// of IPC dispatchers which are keyed on the IPC tag so we normally delegate -// to the appropriate dispatcher unless we can handle the IPC call ourselves. -Dispatcher* PolicyBase::OnMessageReady(IPCParams* ipc, - CallbackGeneric* callback) { - DCHECK(callback); - static const IPCParams ping1 = {IPC_PING1_TAG, ULONG_TYPE}; - static const IPCParams ping2 = {IPC_PING2_TAG, INOUTPTR_TYPE}; - - if (ping1.Matches(ipc) || ping2.Matches(ipc)) { - *callback = reinterpret_cast( - static_cast(&PolicyBase::Ping)); - return this; - } - - Dispatcher* dispatch = GetDispatcher(ipc->ipc_tag); - if (!dispatch) { - NOTREACHED(); - return NULL; - } - return dispatch->OnMessageReady(ipc, callback); -} - -// Delegate to the appropriate dispatcher. -bool PolicyBase::SetupService(InterceptionManager* manager, int service) { - if (IPC_PING1_TAG == service || IPC_PING2_TAG == service) - return true; - - Dispatcher* dispatch = GetDispatcher(service); - if (!dispatch) { - NOTREACHED(); - return false; - } - return dispatch->SetupService(manager, service); -} - -DWORD PolicyBase::MakeJobObject(HANDLE* job) { - // Create the windows job object. - Job job_obj; - DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_); - if (ERROR_SUCCESS != result) { - return result; - } - *job = job_obj.Detach(); - return ERROR_SUCCESS; -} - -DWORD PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) { - // Create the 'naked' token. This will be the permanent token associated - // with the process and therefore with any thread that is not impersonating. - DWORD result = CreateRestrictedToken(lockdown, lockdown_level_, - integrity_level_, PRIMARY); - if (ERROR_SUCCESS != result) { - return result; - } - // Create the 'better' token. We use this token as the one that the main - // thread uses when booting up the process. It should contain most of - // what we need (before reaching main( )) - result = CreateRestrictedToken(initial, initial_level_, - integrity_level_, IMPERSONATION); - if (ERROR_SUCCESS != result) { - ::CloseHandle(*lockdown); - return result; - } - return SBOX_ALL_OK; -} - -bool PolicyBase::AddTarget(TargetProcess* target) { - if (NULL != policy_) - policy_maker_->Done(); - - if (!SetupAllInterceptions(target)) - return false; - - if (!SetupHandleCloser(target)) - return false; - - // Initialize the sandbox infrastructure for the target. - if (ERROR_SUCCESS != target->Init(this, policy_, kIPCMemSize, kPolMemSize)) - return false; - - g_shared_delayed_integrity_level = delayed_integrity_level_; - ResultCode ret = target->TransferVariable( - "g_shared_delayed_integrity_level", - &g_shared_delayed_integrity_level, - sizeof(g_shared_delayed_integrity_level)); - g_shared_delayed_integrity_level = INTEGRITY_LEVEL_LAST; - if (SBOX_ALL_OK != ret) - return false; - - AutoLock lock(&lock_); - targets_.push_back(target); - return true; -} - -bool PolicyBase::OnJobEmpty(HANDLE job) { - AutoLock lock(&lock_); - TargetSet::iterator it; - for (it = targets_.begin(); it != targets_.end(); ++it) { - if ((*it)->Job() == job) - break; - } - if (it == targets_.end()) { - return false; - } - TargetProcess* target = *it; - targets_.erase(it); - delete target; - return true; -} - -EvalResult PolicyBase::EvalPolicy(int service, - CountedParameterSetBase* params) { - if (NULL != policy_) { - if (NULL == policy_->entry[service]) { - // There is no policy for this particular service. This is not a big - // deal. - return DENY_ACCESS; - } - for (int i = 0; i < params->count; i++) { - if (!params->parameters[i].IsValid()) { - NOTREACHED(); - return SIGNAL_ALARM; - } - } - PolicyProcessor pol_evaluator(policy_->entry[service]); - PolicyResult result = pol_evaluator.Evaluate(kShortEval, - params->parameters, - params->count); - if (POLICY_MATCH == result) { - return pol_evaluator.GetAction(); - } - DCHECK(POLICY_ERROR != result); - } - - return DENY_ACCESS; -} - -// We service IPC_PING_TAG message which is a way to test a round trip of the -// IPC subsystem. We receive a integer cookie and we are expected to return the -// cookie times two (or three) and the current tick count. -bool PolicyBase::Ping(IPCInfo* ipc, void* arg1) { - switch (ipc->ipc_tag) { - case IPC_PING1_TAG: { - IPCInt ipc_int(arg1); - uint32 cookie = ipc_int.As32Bit(); - ipc->return_info.extended_count = 2; - ipc->return_info.extended[0].unsigned_int = ::GetTickCount(); - ipc->return_info.extended[1].unsigned_int = 2 * cookie; - return true; - } - case IPC_PING2_TAG: { - CountedBuffer* io_buffer = reinterpret_cast(arg1); - if (sizeof(uint32) != io_buffer->Size()) - return false; - - uint32* cookie = reinterpret_cast(io_buffer->Buffer()); - *cookie = (*cookie) * 3; - return true; - } - default: return false; - } -} - -Dispatcher* PolicyBase::GetDispatcher(int ipc_tag) { - if (ipc_tag >= IPC_LAST_TAG || ipc_tag <= IPC_UNUSED_TAG) - return NULL; - - return ipc_targets_[ipc_tag]; -} - -bool PolicyBase::SetupAllInterceptions(TargetProcess* target) { - InterceptionManager manager(target, relaxed_interceptions_); - - if (policy_) { - for (int i = 0; i < IPC_LAST_TAG; i++) { - if (policy_->entry[i] && !ipc_targets_[i]->SetupService(&manager, i)) - return false; - } - } - - if (!blacklisted_dlls_.empty()) { - std::vector::iterator it = blacklisted_dlls_.begin(); - for (; it != blacklisted_dlls_.end(); ++it) { - manager.AddToUnloadModules(it->c_str()); - } - } - - if (!handle_closer_.SetupHandleInterceptions(&manager)) - return false; - - if (!SetupBasicInterceptions(&manager)) - return false; - - if (!manager.InitializeInterceptions()) - return false; - - // Finally, setup imports on the target so the interceptions can work. - return SetupNtdllImports(target); -} - -bool PolicyBase::SetupHandleCloser(TargetProcess* target) { - return handle_closer_.InitializeTargetHandles(target); -} - -} // namespace sandbox diff --git a/sandbox/src/sandbox_policy_base.h b/sandbox/src/sandbox_policy_base.h deleted file mode 100644 index b3ea805..0000000 --- a/sandbox/src/sandbox_policy_base.h +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SANDBOX_POLICY_BASE_H_ -#define SANDBOX_SRC_SANDBOX_POLICY_BASE_H_ - -#include - -#include -#include - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/string16.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/handle_closer.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_engine_opcodes.h" -#include "sandbox/src/policy_engine_params.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/win_utils.h" - -namespace sandbox { - -class LowLevelPolicy; -class TargetProcess; -struct PolicyGlobal; - -// We act as a policy dispatcher, implementing the handler for the "ping" IPC, -// so we have to provide the appropriate handler on the OnMessageReady method. -// There is a static_cast for the handler, and the compiler only performs the -// cast if the first base class is Dispatcher. -class PolicyBase : public Dispatcher, public TargetPolicy { - public: - PolicyBase(); - - // TargetPolicy: - virtual void AddRef() OVERRIDE; - virtual void Release() OVERRIDE; - virtual ResultCode SetTokenLevel(TokenLevel initial, - TokenLevel lockdown) OVERRIDE; - virtual ResultCode SetJobLevel(JobLevel job_level, - uint32 ui_exceptions) OVERRIDE; - virtual ResultCode SetAlternateDesktop(bool alternate_winstation) OVERRIDE; - virtual std::wstring GetAlternateDesktop() const OVERRIDE; - virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) OVERRIDE; - virtual void DestroyAlternateDesktop() OVERRIDE; - virtual ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) OVERRIDE; - virtual ResultCode SetDelayedIntegrityLevel( - IntegrityLevel integrity_level) OVERRIDE; - virtual void SetStrictInterceptions() OVERRIDE; - virtual ResultCode AddRule(SubSystem subsystem, Semantics semantics, - const wchar_t* pattern) OVERRIDE; - virtual ResultCode AddDllToUnload(const wchar_t* dll_name); - virtual ResultCode AddKernelObjectToClose(const char16* handle_type, - const char16* handle_name) OVERRIDE; - - // Dispatcher: - virtual Dispatcher* OnMessageReady(IPCParams* ipc, - CallbackGeneric* callback) OVERRIDE; - virtual bool SetupService(InterceptionManager* manager, int service) OVERRIDE; - - // Creates a Job object with the level specified in a previous call to - // SetJobLevel(). Returns the standard windows of ::GetLastError(). - DWORD MakeJobObject(HANDLE* job); - - // Creates the two tokens with the levels specified in a previous call to - // SetTokenLevel(). Returns the standard windows of ::GetLastError(). - DWORD MakeTokens(HANDLE* initial, HANDLE* lockdown); - - // Adds a target process to the internal list of targets. Internally a - // call to TargetProcess::Init() is issued. - bool AddTarget(TargetProcess* target); - - // Called when there are no more active processes in a Job. - // Removes a Job object associated with this policy and the target associated - // with the job. - bool OnJobEmpty(HANDLE job); - - EvalResult EvalPolicy(int service, CountedParameterSetBase* params); - - private: - ~PolicyBase(); - - // Test IPC providers. - bool Ping(IPCInfo* ipc, void* cookie); - - // Returns a dispatcher from ipc_targets_. - Dispatcher* GetDispatcher(int ipc_tag); - - // Sets up interceptions for a new target. - bool SetupAllInterceptions(TargetProcess* target); - - // Sets up the handle closer for a new target. - bool SetupHandleCloser(TargetProcess* target); - - // This lock synchronizes operations on the targets_ collection. - CRITICAL_SECTION lock_; - // Maintains the list of target process associated with this policy. - // The policy takes ownership of them. - typedef std::list TargetSet; - TargetSet targets_; - // Standard object-lifetime reference counter. - volatile LONG ref_count; - // The user-defined global policy settings. - TokenLevel lockdown_level_; - TokenLevel initial_level_; - JobLevel job_level_; - uint32 ui_exceptions_; - bool use_alternate_desktop_; - bool use_alternate_winstation_; - // Helps the file system policy initialization. - bool file_system_init_; - bool relaxed_interceptions_; - IntegrityLevel integrity_level_; - IntegrityLevel delayed_integrity_level_; - // The array of objects that will answer IPC calls. - Dispatcher* ipc_targets_[IPC_LAST_TAG]; - // Object in charge of generating the low level policy. - LowLevelPolicy* policy_maker_; - // Memory structure that stores the low level policy. - PolicyGlobal* policy_; - // The list of dlls to unload in the target process. - std::vector blacklisted_dlls_; - // This is a map of handle-types to names that we need to close in the - // target process. A null set means we need to close all handles of the - // given type. - HandleCloser handle_closer_; - - static HDESK alternate_desktop_handle_; - static HWINSTA alternate_winstation_handle_; - - DISALLOW_COPY_AND_ASSIGN(PolicyBase); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SANDBOX_POLICY_BASE_H_ diff --git a/sandbox/src/sandbox_types.h b/sandbox/src/sandbox_types.h deleted file mode 100644 index ce9b767..0000000 --- a/sandbox/src/sandbox_types.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SANDBOX_TYPES_H_ -#define SANDBOX_SRC_SANDBOX_TYPES_H_ - -namespace sandbox { - -// Operation result codes returned by the sandbox API. -enum ResultCode { - SBOX_ALL_OK = 0, - // Error is originating on the win32 layer. Call GetlastError() for more - // information. - SBOX_ERROR_GENERIC = 1, - // An invalid combination of parameters was given to the API. - SBOX_ERROR_BAD_PARAMS = 2, - // The desired operation is not supported at this time. - SBOX_ERROR_UNSUPPORTED = 3, - // The request requires more memory that allocated or available. - SBOX_ERROR_NO_SPACE = 4, - // The ipc service requested does not exist. - SBOX_ERROR_INVALID_IPC = 5, - // The ipc service did not complete. - SBOX_ERROR_FAILED_IPC = 6, - // The requested handle was not found. - SBOX_ERROR_NO_HANDLE = 7, - // This function was not expected to be called at this time. - SBOX_ERROR_UNEXPECTED_CALL = 8, - // WaitForAllTargets is already called. - SBOX_ERROR_WAIT_ALREADY_CALLED = 9, - // A channel error prevented DoCall from executing. - SBOX_ERROR_CHANNEL_ERROR = 10, - // Failed to create the alternate desktop. - SBOX_ERROR_CANNOT_CREATE_DESKTOP = 11, - // Failed to create the alternate window station. - SBOX_ERROR_CANNOT_CREATE_WINSTATION = 12, - // Failed to switch back to the interactive window station. - SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION = 13, - // Placeholder for last item of the enum. - SBOX_ERROR_LAST -}; - -// If the sandbox cannot create a secure environment for the target, the -// target will be forcibly terminated. These are the process exit codes. -enum TerminationCodes { - SBOX_FATAL_INTEGRITY = 7006, // Could not set the integrity level. - SBOX_FATAL_DROPTOKEN = 7007, // Could not lower the token. - SBOX_FATAL_FLUSHANDLES = 7008, // Failed to flush registry handles. - SBOX_FATAL_CACHEDISABLE = 7009, // Failed to forbid HCKU caching. - SBOX_FATAL_CLOSEHANDLES = 7010 // Failed to close pending handles. -}; - -class BrokerServices; -class TargetServices; - -// Contains the pointer to a target or broker service. -struct SandboxInterfaceInfo { - BrokerServices* broker_services; - TargetServices* target_services; -}; - -#if SANDBOX_EXPORTS -#define SANDBOX_INTERCEPT extern "C" __declspec(dllexport) -#else -#define SANDBOX_INTERCEPT extern "C" -#endif - -enum InterceptionType { - INTERCEPTION_INVALID = 0, - INTERCEPTION_SERVICE_CALL, // Trampoline of an NT native call - INTERCEPTION_EAT, - INTERCEPTION_SIDESTEP, // Preamble patch - INTERCEPTION_SMART_SIDESTEP, // Preamble patch but bypass internal calls - INTERCEPTION_UNLOAD_MODULE, // Unload the module (don't patch) - INTERCEPTION_LAST // Placeholder for last item in the enumeration -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SANDBOX_TYPES_H_ diff --git a/sandbox/src/sandbox_utils.cc b/sandbox/src/sandbox_utils.cc deleted file mode 100644 index 3bfa696..0000000 --- a/sandbox/src/sandbox_utils.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/sandbox_utils.h" - -#include - -#include "base/logging.h" -#include "base/win/windows_version.h" -#include "sandbox/src/internal_types.h" -#include "sandbox/src/nt_internals.h" - -namespace sandbox { - -bool GetModuleHandleHelper(DWORD flags, const wchar_t* module_name, - HMODULE* module) { - DCHECK(module); - - HMODULE kernel32_base = ::GetModuleHandle(kKerneldllName); - if (!kernel32_base) { - NOTREACHED(); - return false; - } - - GetModuleHandleExFunction get_module_handle_ex = reinterpret_cast< - GetModuleHandleExFunction>(::GetProcAddress(kernel32_base, - "GetModuleHandleExW")); - if (get_module_handle_ex) { - BOOL ret = get_module_handle_ex(flags, module_name, module); - return (ret ? true : false); - } - - if (!flags) { - *module = ::LoadLibrary(module_name); - } else if (flags & GET_MODULE_HANDLE_EX_FLAG_PIN) { - NOTREACHED(); - return false; - } else if (!(flags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)) { - DCHECK((flags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT) == - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT); - - *module = ::GetModuleHandle(module_name); - } else { - DCHECK((flags & (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)) == - (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)); - - MEMORY_BASIC_INFORMATION info = {0}; - size_t returned = VirtualQuery(module_name, &info, sizeof(info)); - if (sizeof(info) != returned) - return false; - *module = reinterpret_cast(info.AllocationBase); - } - return true; -} - -bool IsXPSP2OrLater() { - base::win::Version version = base::win::GetVersion(); - return (version > base::win::VERSION_XP) || - ((version == base::win::VERSION_XP) && - (base::win::OSInfo::GetInstance()->service_pack().major >= 2)); -} - -void InitObjectAttribs(const std::wstring& name, ULONG attributes, HANDLE root, - OBJECT_ATTRIBUTES* obj_attr, UNICODE_STRING* uni_name) { - static RtlInitUnicodeStringFunction RtlInitUnicodeString; - if (!RtlInitUnicodeString) { - HMODULE ntdll = ::GetModuleHandle(kNtdllName); - RtlInitUnicodeString = reinterpret_cast( - GetProcAddress(ntdll, "RtlInitUnicodeString")); - DCHECK(RtlInitUnicodeString); - } - RtlInitUnicodeString(uni_name, name.c_str()); - InitializeObjectAttributes(obj_attr, uni_name, attributes, root, NULL); -} - -}; // namespace sandbox diff --git a/sandbox/src/sandbox_utils.h b/sandbox/src/sandbox_utils.h deleted file mode 100644 index 314330f..0000000 --- a/sandbox/src/sandbox_utils.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SANDBOX_UTILS_H__ -#define SANDBOX_SRC_SANDBOX_UTILS_H__ - -#include -#include - -#include "base/basictypes.h" -#include "sandbox/src/nt_internals.h" - -namespace sandbox { - -typedef BOOL (WINAPI* GetModuleHandleExFunction)(DWORD flags, - LPCWSTR module_name, - HMODULE* module); - -// Windows XP provides a nice function in kernel32.dll called GetModuleHandleEx -// This function allows us to verify if a function exported by the module -// lies in the module itself. -// As we need compatibility with windows 2000, we cannot use this function -// by calling it by name. This helper function checks if the GetModuleHandleEx -// function is exported by kernel32 and uses it, otherwise, implemets part of -// the functionality exposed by GetModuleHandleEx. -bool GetModuleHandleHelper(DWORD flags, const wchar_t* module_name, - HMODULE* module); - -// Returns true if the current OS is Windows XP SP2 or later. -bool IsXPSP2OrLater(); - -void InitObjectAttribs(const std::wstring& name, ULONG attributes, HANDLE root, - OBJECT_ATTRIBUTES* obj_attr, UNICODE_STRING* uni_name); - -}; // namespace sandbox - -#endif // SANDBOX_SRC_SANDBOX_UTILS_H__ diff --git a/sandbox/src/security_level.h b/sandbox/src/security_level.h deleted file mode 100644 index 467f96f..0000000 --- a/sandbox/src/security_level.h +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SECURITY_LEVEL_H_ -#define SANDBOX_SRC_SECURITY_LEVEL_H_ - -namespace sandbox { - -// List of all the integrity levels supported in the sandbox. This is used -// only on Windows Vista. You can't set the integrity level of the process -// in the sandbox to a level higher than yours. -enum IntegrityLevel { - INTEGRITY_LEVEL_SYSTEM, - INTEGRITY_LEVEL_HIGH, - INTEGRITY_LEVEL_MEDIUM, - INTEGRITY_LEVEL_MEDIUM_LOW, - INTEGRITY_LEVEL_LOW, - INTEGRITY_LEVEL_BELOW_LOW, - INTEGRITY_LEVEL_UNTRUSTED, - INTEGRITY_LEVEL_LAST -}; - -// The Token level specifies a set of security profiles designed to -// provide the bulk of the security of sandbox. -// -// TokenLevel |Restricting |Deny Only |Privileges| -// |Sids |Sids | | -// ----------------------------|--------------|----------------|----------| -// USER_LOCKDOWN | Null Sid | All | None | -// ----------------------------|--------------|----------------|----------| -// USER_RESTRICTED | RESTRICTED | All | Traverse | -// ----------------------------|--------------|----------------|----------| -// USER_LIMITED | Users | All except: | Traverse | -// | Everyone | Users | | -// | RESTRICTED | Everyone | | -// | | Interactive | | -// ----------------------------|--------------|----------------|----------| -// USER_INTERACTIVE | Users | All except: | Traverse | -// | Everyone | Users | | -// | RESTRICTED | Everyone | | -// | Owner | Interactive | | -// | | Local | | -// | | Authent-users | | -// | | User | | -// ----------------------------|--------------|----------------|----------| -// USER_NON_ADMIN | None | All except: | Traverse | -// | | Users | | -// | | Everyone | | -// | | Interactive | | -// | | Local | | -// | | Authent-users | | -// | | User | | -// ----------------------------|--------------|----------------|----------| -// USER_RESTRICTED_SAME_ACCESS | All | None | All | -// ----------------------------|--------------|----------------|----------| -// USER_UNPROTECTED | None | None | All | -// ----------------------------|--------------|----------------|----------| -// -// The above restrictions are actually a transformation that is applied to -// the existing broker process token. The resulting token that will be -// applied to the target process depends both on the token level selected -// and on the broker token itself. -// -// The LOCKDOWN and RESTRICTED are designed to allow access to almost -// nothing that has security associated with and they are the recommended -// levels to run sandboxed code specially if there is a chance that the -// broker is process might be started by a user that belongs to the Admins -// or power users groups. -enum TokenLevel { - USER_LOCKDOWN = 0, - USER_RESTRICTED, - USER_LIMITED, - USER_INTERACTIVE, - USER_NON_ADMIN, - USER_RESTRICTED_SAME_ACCESS, - USER_UNPROTECTED -}; - -// The Job level specifies a set of decreasing security profiles for the -// Job object that the target process will be placed into. -// This table summarizes the security associated with each level: -// -// JobLevel |General |Quota | -// |restrictions |restrictions | -// -----------------|---------------------------------- |--------------------| -// JOB_UNPROTECTED | None | *Kill on Job close.| -// -----------------|---------------------------------- |--------------------| -// JOB_INTERACTIVE | *Forbid system-wide changes using | | -// | SystemParametersInfo(). | *Kill on Job close.| -// | *Forbid the creation/switch of | | -// | Desktops. | | -// | *Forbids calls to ExitWindows(). | | -// -----------------|---------------------------------- |--------------------| -// JOB_LIMITED_USER | Same as INTERACTIVE_USER plus: | *One active process| -// | *Forbid changes to the display | limit. | -// | settings. | *Kill on Job close.| -// -----------------|---------------------------------- |--------------------| -// JOB_RESTRICTED | Same as LIMITED_USER plus: | *One active process| -// | * No read/write to the clipboard. | limit. | -// | * No access to User Handles that | *Kill on Job close.| -// | belong to other processes. | | -// | * Forbid message broadcasts. | | -// | * Forbid setting global hooks. | | -// | * No access to the global atoms | | -// | table. | | -// -----------------|-----------------------------------|--------------------| -// JOB_LOCKDOWN | Same as RESTRICTED | *One active process| -// | | limit. | -// | | *Kill on Job close.| -// | | *Kill on unhandled | -// | | exception. | -// | | | -// In the context of the above table, 'user handles' refers to the handles of -// windows, bitmaps, menus, etc. Files, treads and registry handles are kernel -// handles and are not affected by the job level settings. -enum JobLevel { - JOB_LOCKDOWN = 0, - JOB_RESTRICTED, - JOB_LIMITED_USER, - JOB_INTERACTIVE, - JOB_UNPROTECTED -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SECURITY_LEVEL_H_ diff --git a/sandbox/src/service_resolver.cc b/sandbox/src/service_resolver.cc deleted file mode 100644 index 79579a0..0000000 --- a/sandbox/src/service_resolver.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/service_resolver.h" - -#include "base/logging.h" -#include "base/win/pe_image.h" - -namespace sandbox { - -NTSTATUS ServiceResolverThunk::ResolveInterceptor( - const void* interceptor_module, - const char* interceptor_name, - const void** address) { - // After all, we are using a locally mapped version of the exe, so the - // action is the same as for a target function. - return ResolveTarget(interceptor_module, interceptor_name, - const_cast(address)); -} - -// In this case all the work is done from the parent, so resolve is -// just a simple GetProcAddress. -NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module, - const char* function_name, - void** address) { - DCHECK(address); - if (NULL == module) - return STATUS_UNSUCCESSFUL; - - base::win::PEImage module_image(module); - *address = module_image.GetProcAddress(function_name); - - if (NULL == *address) { - NOTREACHED(); - return STATUS_UNSUCCESSFUL; - } - - return STATUS_SUCCESS; -} - -} // namespace sandbox diff --git a/sandbox/src/service_resolver.h b/sandbox/src/service_resolver.h deleted file mode 100644 index 99eadb6..0000000 --- a/sandbox/src/service_resolver.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SERVICE_RESOLVER_H__ -#define SANDBOX_SRC_SERVICE_RESOLVER_H__ - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/resolver.h" - -namespace sandbox { - -// This is the concrete resolver used to perform service-call type functions -// inside ntdll.dll. -class ServiceResolverThunk : public ResolverThunk { - public: - // The service resolver needs a child process to write to. - ServiceResolverThunk(HANDLE process, bool relaxed) - : process_(process), ntdll_base_(NULL), win2k_(false), - relaxed_(relaxed), relative_jump_(0) {} - virtual ~ServiceResolverThunk() {} - - // Implementation of Resolver::Setup. - virtual NTSTATUS Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used); - - // Implementation of Resolver::ResolveInterceptor. - virtual NTSTATUS ResolveInterceptor(const void* module, - const char* function_name, - const void** address); - - // Implementation of Resolver::ResolveTarget. - virtual NTSTATUS ResolveTarget(const void* module, - const char* function_name, - void** address); - - // Implementation of Resolver::GetThunkSize. - virtual size_t GetThunkSize() const; - - protected: - // The unit test will use this member to allow local patch on a buffer. - HMODULE ntdll_base_; - - // Handle of the child process. - HANDLE process_; - - protected: - // Keeps track of a Windows 2000 resolver. - bool win2k_; - - private: - // Returns true if the code pointer by target_ corresponds to the expected - // type of function. Saves that code on the first part of the thunk pointed - // by local_thunk (should be directly accessible from the parent). - virtual bool IsFunctionAService(void* local_thunk) const; - - // Performs the actual patch of target_. - // local_thunk must be already fully initialized, and the first part must - // contain the original code. The real type of this buffer is ServiceFullThunk - // (yes, private). remote_thunk (real type ServiceFullThunk), must be - // allocated on the child, and will contain the thunk data, after this call. - // Returns the apropriate status code. - virtual NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk); - - // Provides basically the same functionality as IsFunctionAService but it - // continues even if it does not recognize the function code. remote_thunk - // is the address of our memory on the child. - bool SaveOriginalFunction(void* local_thunk, void* remote_thunk); - - // true if we are allowed to patch already-patched functions. - bool relaxed_; - ULONG relative_jump_; - - DISALLOW_COPY_AND_ASSIGN(ServiceResolverThunk); -}; - -// This is the concrete resolver used to perform service-call type functions -// inside ntdll.dll on WOW64 (32 bit ntdll on 64 bit Vista). -class Wow64ResolverThunk : public ServiceResolverThunk { - public: - // The service resolver needs a child process to write to. - Wow64ResolverThunk(HANDLE process, bool relaxed) - : ServiceResolverThunk(process, relaxed) {} - virtual ~Wow64ResolverThunk() {} - - private: - virtual bool IsFunctionAService(void* local_thunk) const; - - DISALLOW_COPY_AND_ASSIGN(Wow64ResolverThunk); -}; - -// This is the concrete resolver used to perform service-call type functions -// inside ntdll.dll on WOW64 for Windows 8. -class Wow64W8ResolverThunk : public ServiceResolverThunk { - public: - // The service resolver needs a child process to write to. - Wow64W8ResolverThunk(HANDLE process, bool relaxed) - : ServiceResolverThunk(process, relaxed) {} - virtual ~Wow64W8ResolverThunk() {} - - private: - virtual bool IsFunctionAService(void* local_thunk) const; - - DISALLOW_COPY_AND_ASSIGN(Wow64W8ResolverThunk); -}; - -// This is the concrete resolver used to perform service-call type functions -// inside ntdll.dll on Windows 2000 and XP pre SP2. -class Win2kResolverThunk : public ServiceResolverThunk { - public: - // The service resolver needs a child process to write to. - Win2kResolverThunk(HANDLE process, bool relaxed) - : ServiceResolverThunk(process, relaxed) { - win2k_ = true; - } - virtual ~Win2kResolverThunk() {} - - private: - virtual bool IsFunctionAService(void* local_thunk) const; - - DISALLOW_COPY_AND_ASSIGN(Win2kResolverThunk); -}; - -// This is the concrete resolver used to perform service-call type functions -// inside ntdll.dll on Windows 8. -class Win8ResolverThunk : public ServiceResolverThunk { - public: - // The service resolver needs a child process to write to. - Win8ResolverThunk(HANDLE process, bool relaxed) - : ServiceResolverThunk(process, relaxed) {} - virtual ~Win8ResolverThunk() {} - - private: - virtual bool IsFunctionAService(void* local_thunk) const; - - DISALLOW_COPY_AND_ASSIGN(Win8ResolverThunk); -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_SERVICE_RESOLVER_H__ diff --git a/sandbox/src/service_resolver_32.cc b/sandbox/src/service_resolver_32.cc deleted file mode 100644 index 2f5597b..0000000 --- a/sandbox/src/service_resolver_32.cc +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/service_resolver.h" - -#include "base/memory/scoped_ptr.h" -#include "sandbox/src/sandbox_utils.h" -#include "sandbox/src/win_utils.h" - -namespace { -#pragma pack(push, 1) - -const BYTE kMovEax = 0xB8; -const BYTE kMovEdx = 0xBA; -const USHORT kMovEdxEsp = 0xD48B; -const USHORT kCallPtrEdx = 0x12FF; -const USHORT kCallEdx = 0xD2FF; -const BYTE kCallEip = 0xE8; -const BYTE kRet = 0xC2; -const BYTE kRet2 = 0xC3; -const BYTE kNop = 0x90; -const USHORT kJmpEdx = 0xE2FF; -const USHORT kXorEcx = 0xC933; -const ULONG kLeaEdx = 0x0424548D; -const ULONG kCallFs1 = 0xC015FF64; -const USHORT kCallFs2 = 0; -const BYTE kCallFs3 = 0; -const BYTE kAddEsp1 = 0x83; -const USHORT kAddEsp2 = 0x4C4; -const BYTE kJmp32 = 0xE9; -const USHORT kSysenter = 0x340F; - -const int kMaxService = 1000; - -// Service code for 32 bit systems. -// NOTE: on win2003 "call dword ptr [edx]" is "call edx". -struct ServiceEntry { - // This struct contains roughly the following code: - // 00 mov eax,25h - // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300) - // 0a call dword ptr [edx] - // 0c ret 2Ch - // 0f nop - BYTE mov_eax; // = B8 - ULONG service_id; - BYTE mov_edx; // = BA - ULONG stub; - USHORT call_ptr_edx; // = FF 12 - BYTE ret; // = C2 - USHORT num_params; - BYTE nop; -}; - -// Service code for 32 bit Windows 8. -struct ServiceEntryW8 { - // This struct contains the following code: - // 00 b825000000 mov eax,25h - // 05 e803000000 call eip+3 - // 0a c22c00 ret 2Ch - // 0d 8bd4 mov edx,esp - // 0f 0f34 sysenter - // 11 c3 ret - // 12 8bff mov edi,edi - BYTE mov_eax; // = B8 - ULONG service_id; - BYTE call_eip; // = E8 - ULONG call_offset; - BYTE ret_p; // = C2 - USHORT num_params; - USHORT mov_edx_esp; // = BD D4 - USHORT sysenter; // = 0F 34 - BYTE ret; // = C3 - USHORT nop; -}; - -// Service code for a 32 bit process running on a 64 bit os. -struct Wow64Entry { - // This struct may contain one of two versions of code: - // 1. For XP, Vista and 2K3: - // 00 b825000000 mov eax, 25h - // 05 33c9 xor ecx, ecx - // 07 8d542404 lea edx, [esp + 4] - // 0b 64ff15c0000000 call dword ptr fs:[0C0h] - // 12 c22c00 ret 2Ch - // - // 2. For Windows 7: - // 00 b825000000 mov eax, 25h - // 05 33c9 xor ecx, ecx - // 07 8d542404 lea edx, [esp + 4] - // 0b 64ff15c0000000 call dword ptr fs:[0C0h] - // 12 83c404 add esp, 4 - // 15 c22c00 ret 2Ch - // - // So we base the structure on the bigger one: - BYTE mov_eax; // = B8 - ULONG service_id; - USHORT xor_ecx; // = 33 C9 - ULONG lea_edx; // = 8D 54 24 04 - ULONG call_fs1; // = 64 FF 15 C0 - USHORT call_fs2; // = 00 00 - BYTE call_fs3; // = 00 - BYTE add_esp1; // = 83 or ret - USHORT add_esp2; // = C4 04 or num_params - BYTE ret; // = C2 - USHORT num_params; -}; - -// Service code for a 32 bit process running on 64 bit Windows 8. -struct Wow64EntryW8 { - // 00 b825000000 mov eax, 25h - // 05 64ff15c0000000 call dword ptr fs:[0C0h] - // 0b c22c00 ret 2Ch - // 0f 90 nop - BYTE mov_eax; // = B8 - ULONG service_id; - ULONG call_fs1; // = 64 FF 15 C0 - USHORT call_fs2; // = 00 00 - BYTE call_fs3; // = 00 - BYTE ret; // = C2 - USHORT num_params; - BYTE nop; -}; - -// Make sure that relaxed patching works as expected. -const size_t kMinServiceSize = offsetof(ServiceEntry, ret); -COMPILE_ASSERT(sizeof(ServiceEntryW8) >= kMinServiceSize, wrong_service_len); -COMPILE_ASSERT(sizeof(Wow64Entry) >= kMinServiceSize, wrong_service_len); -COMPILE_ASSERT(sizeof(Wow64EntryW8) >= kMinServiceSize, wrong_service_len); - -struct ServiceFullThunk { - union { - ServiceEntry original; - ServiceEntryW8 original_w8; - Wow64Entry wow_64; - Wow64EntryW8 wow_64_w8; - }; - int internal_thunk; // Dummy member to the beginning of the internal thunk. -}; - -#pragma pack(pop) - -}; // namespace - -namespace sandbox { - -NTSTATUS ServiceResolverThunk::Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) { - NTSTATUS ret = Init(target_module, interceptor_module, target_name, - interceptor_name, interceptor_entry_point, - thunk_storage, storage_bytes); - if (!NT_SUCCESS(ret)) - return ret; - - relative_jump_ = 0; - size_t thunk_bytes = GetThunkSize(); - scoped_array thunk_buffer(new char[thunk_bytes]); - ServiceFullThunk* thunk = reinterpret_cast( - thunk_buffer.get()); - - if (!IsFunctionAService(&thunk->original) && - (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) - return STATUS_UNSUCCESSFUL; - - ret = PerformPatch(thunk, thunk_storage); - - if (NULL != storage_used) - *storage_used = thunk_bytes; - - return ret; -} - -size_t ServiceResolverThunk::GetThunkSize() const { - return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize(); -} - -bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { - ServiceEntry function_code; - SIZE_T read; - if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) - return false; - - if (sizeof(function_code) != read) - return false; - - if (kMovEax != function_code.mov_eax || - kMovEdx != function_code.mov_edx || - (kCallPtrEdx != function_code.call_ptr_edx && - kCallEdx != function_code.call_ptr_edx) || - kRet != function_code.ret) - return false; - - // Find the system call pointer if we don't already have it. - if (kCallEdx != function_code.call_ptr_edx) { - DWORD ki_system_call; - if (!::ReadProcessMemory(process_, - bit_cast(function_code.stub), - &ki_system_call, sizeof(ki_system_call), &read)) - return false; - - if (sizeof(ki_system_call) != read) - return false; - - HMODULE module_1, module_2; - // last check, call_stub should point to a KiXXSystemCall function on ntdll - if (!GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - bit_cast(ki_system_call), - &module_1)) - return false; - - if (NULL != ntdll_base_) { - // This path is only taken when running the unit tests. We want to be - // able to patch a buffer in memory, so target_ is not inside ntdll. - module_2 = ntdll_base_; - } else { - if (!GetModuleHandleHelper( - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - reinterpret_cast(target_), - &module_2)) { - return false; - } - } - - if (module_1 != module_2) - return false; - } - - // Save the verified code - memcpy(local_thunk, &function_code, sizeof(function_code)); - - return true; -} - -NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, - void* remote_thunk) { - ServiceEntry intercepted_code; - size_t bytes_to_write = sizeof(intercepted_code); - ServiceFullThunk *full_local_thunk = reinterpret_cast( - local_thunk); - ServiceFullThunk *full_remote_thunk = reinterpret_cast( - remote_thunk); - - // patch the original code - memcpy(&intercepted_code, &full_local_thunk->original, - sizeof(intercepted_code)); - intercepted_code.mov_eax = kMovEax; - intercepted_code.service_id = full_local_thunk->original.service_id; - intercepted_code.mov_edx = kMovEdx; - intercepted_code.stub = bit_cast(&full_remote_thunk->internal_thunk); - intercepted_code.call_ptr_edx = kJmpEdx; - bytes_to_write = kMinServiceSize; - - if (relative_jump_) { - intercepted_code.mov_eax = kJmp32; - intercepted_code.service_id = relative_jump_; - bytes_to_write = offsetof(ServiceEntry, mov_edx); - } - - // setup the thunk - SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(), - remote_thunk, interceptor_); - - size_t thunk_size = GetThunkSize(); - - // copy the local thunk buffer to the child - SIZE_T written; - if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, - thunk_size, &written)) - return STATUS_UNSUCCESSFUL; - - if (thunk_size != written) - return STATUS_UNSUCCESSFUL; - - // and now change the function to intercept, on the child - if (NULL != ntdll_base_) { - // running a unit test - if (!::WriteProcessMemory(process_, target_, &intercepted_code, - bytes_to_write, &written)) - return STATUS_UNSUCCESSFUL; - } else { - if (!WriteProtectedChildMemory(process_, target_, &intercepted_code, - bytes_to_write)) - return STATUS_UNSUCCESSFUL; - } - - return STATUS_SUCCESS; -} - -bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk, - void* remote_thunk) { - ServiceEntry function_code; - SIZE_T read; - if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) - return false; - - if (sizeof(function_code) != read) - return false; - - if (kJmp32 == function_code.mov_eax) { - // Plain old entry point patch. The relative jump address follows it. - ULONG relative = function_code.service_id; - - // First, fix our copy of their patch. - relative += bit_cast(target_) - bit_cast(remote_thunk); - - function_code.service_id = relative; - - // And now, remember how to re-patch it. - ServiceFullThunk *full_thunk = - reinterpret_cast(remote_thunk); - - const ULONG kJmp32Size = 5; - - relative_jump_ = bit_cast(&full_thunk->internal_thunk) - - bit_cast(target_) - kJmp32Size; - } - - // Save the verified code - memcpy(local_thunk, &function_code, sizeof(function_code)); - - return true; -} - -bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { - Wow64Entry function_code; - SIZE_T read; - if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) - return false; - - if (sizeof(function_code) != read) - return false; - - if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx || - kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 || - kCallFs2 != function_code.call_fs2 || kCallFs3 != function_code.call_fs3) - return false; - - if ((kAddEsp1 == function_code.add_esp1 && - kAddEsp2 == function_code.add_esp2 && - kRet == function_code.ret) || kRet == function_code.add_esp1) { - // Save the verified code - memcpy(local_thunk, &function_code, sizeof(function_code)); - return true; - } - - return false; -} - -bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const { - Wow64EntryW8 function_code; - SIZE_T read; - if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) - return false; - - if (sizeof(function_code) != read) - return false; - - if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 || - kCallFs2 != function_code.call_fs2 || - kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) { - return false; - } - - // Save the verified code - memcpy(local_thunk, &function_code, sizeof(function_code)); - return true; -} - -bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const { - ServiceEntry function_code; - SIZE_T read; - if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) - return false; - - if (sizeof(function_code) != read) - return false; - - if (kMovEax != function_code.mov_eax || - function_code.service_id > kMaxService) - return false; - - // Save the verified code - memcpy(local_thunk, &function_code, sizeof(function_code)); - - return true; -} - -bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const { - ServiceEntryW8 function_code; - SIZE_T read; - if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) - return false; - - if (sizeof(function_code) != read) - return false; - - if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip || - function_code.call_offset != 3 || kRet != function_code.ret_p || - kMovEdxEsp != function_code.mov_edx_esp || - kSysenter != function_code.sysenter || kRet2 != function_code.ret) { - return false; - } - - // Save the verified code - memcpy(local_thunk, &function_code, sizeof(function_code)); - - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/service_resolver_64.cc b/sandbox/src/service_resolver_64.cc deleted file mode 100644 index 01e3b1a..0000000 --- a/sandbox/src/service_resolver_64.cc +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/service_resolver.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "sandbox/src/win_utils.h" - -namespace { -#pragma pack(push, 1) - -const ULONG kMmovR10EcxMovEax = 0xB8D18B4C; -const USHORT kSyscall = 0x050F; -const BYTE kRetNp = 0xC3; -const ULONG64 kMov1 = 0x54894808244C8948; -const ULONG64 kMov2 = 0x4C182444894C1024; -const ULONG kMov3 = 0x20244C89; - -// Service code for 64 bit systems. -struct ServiceEntry { - // This struct contains roughly the following code: - // 00 mov r10,rcx - // 03 mov eax,52h - // 08 syscall - // 0a ret - // 0b xchg ax,ax - // 0e xchg ax,ax - - ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8 - ULONG service_id; - USHORT syscall; // = 0F 05 - BYTE ret; // = C3 - BYTE pad; // = 66 - USHORT xchg_ax_ax1; // = 66 90 - USHORT xchg_ax_ax2; // = 66 90 -}; - -// Service code for 64 bit Windows 8. -struct ServiceEntryW8 { - // This struct contains the following code: - // 00 48894c2408 mov [rsp+8], rcx - // 05 4889542410 mov [rsp+10], rdx - // 0a 4c89442418 mov [rsp+18], r8 - // 0f 4c894c2420 mov [rsp+20], r9 - // 14 4c8bd1 mov r10,rcx - // 17 b825000000 mov eax,25h - // 1c 0f05 syscall - // 1e c3 ret - // 1f 90 nop - - ULONG64 mov_1; // = 48 89 4C 24 08 48 89 54 - ULONG64 mov_2; // = 24 10 4C 89 44 24 18 4C - ULONG mov_3; // = 89 4C 24 20 - ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8 - ULONG service_id; - USHORT syscall; // = 0F 05 - BYTE ret; // = C2 - BYTE nop; // = 90 -}; - -// We don't have an internal thunk for x64. -struct ServiceFullThunk { - union { - ServiceEntry original; - ServiceEntryW8 original_w8; - }; -}; - -#pragma pack(pop) - -bool IsService(const void* source) { - const ServiceEntry* service = - reinterpret_cast(source); - - return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax && - kSyscall == service->syscall && kRetNp == service->ret); -} - -}; // namespace - -namespace sandbox { - -NTSTATUS ServiceResolverThunk::Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) { - NTSTATUS ret = Init(target_module, interceptor_module, target_name, - interceptor_name, interceptor_entry_point, - thunk_storage, storage_bytes); - if (!NT_SUCCESS(ret)) - return ret; - - size_t thunk_bytes = GetThunkSize(); - scoped_array thunk_buffer(new char[thunk_bytes]); - ServiceFullThunk* thunk = reinterpret_cast( - thunk_buffer.get()); - - if (!IsFunctionAService(&thunk->original)) - return STATUS_UNSUCCESSFUL; - - ret = PerformPatch(thunk, thunk_storage); - - if (NULL != storage_used) - *storage_used = thunk_bytes; - - return ret; -} - -size_t ServiceResolverThunk::GetThunkSize() const { - return sizeof(ServiceFullThunk); -} - -bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { - ServiceFullThunk function_code; - SIZE_T read; - if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) - return false; - - if (sizeof(function_code) != read) - return false; - - if (!IsService(&function_code)) { - // See if it's the Win8 signature. - ServiceEntryW8* w8_service = &function_code.original_w8; - if (!IsService(&w8_service->mov_r10_rcx_mov_eax) || - w8_service->mov_1 != kMov1 || w8_service->mov_1 != kMov1 || - w8_service->mov_1 != kMov1) { - return false; - } - } - - // Save the verified code. - memcpy(local_thunk, &function_code, sizeof(function_code)); - - return true; -} - -NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, - void* remote_thunk) { - ServiceFullThunk* full_local_thunk = reinterpret_cast( - local_thunk); - ServiceFullThunk* full_remote_thunk = reinterpret_cast( - remote_thunk); - - // Patch the original code. - ServiceEntry local_service; - DCHECK_GE(GetInternalThunkSize(), sizeof(local_service)); - if (!SetInternalThunk(&local_service, sizeof(local_service), NULL, - interceptor_)) - return STATUS_UNSUCCESSFUL; - - // Copy the local thunk buffer to the child. - SIZE_T actual; - if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, - sizeof(ServiceFullThunk), &actual)) - return STATUS_UNSUCCESSFUL; - - if (sizeof(ServiceFullThunk) != actual) - return STATUS_UNSUCCESSFUL; - - // And now change the function to intercept, on the child. - if (NULL != ntdll_base_) { - // Running a unit test. - if (!::WriteProcessMemory(process_, target_, &local_service, - sizeof(local_service), &actual)) - return STATUS_UNSUCCESSFUL; - } else { - if (!WriteProtectedChildMemory(process_, target_, &local_service, - sizeof(local_service))) - return STATUS_UNSUCCESSFUL; - } - - return STATUS_SUCCESS; -} - -bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { - NOTREACHED(); - return false; -} - -bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const { - NOTREACHED(); - return false; -} - -} // namespace sandbox diff --git a/sandbox/src/service_resolver_unittest.cc b/sandbox/src/service_resolver_unittest.cc deleted file mode 100644 index fc85c86..0000000 --- a/sandbox/src/service_resolver_unittest.cc +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains unit tests for ServiceResolverThunk. - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/win/windows_version.h" -#include "sandbox/src/resolver.h" -#include "sandbox/src/sandbox_utils.h" -#include "sandbox/src/service_resolver.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -// This is the concrete resolver used to perform service-call type functions -// inside ntdll.dll. -template -class ResolverThunkTest : public T { - public: - // The service resolver needs a child process to write to. - explicit ResolverThunkTest(bool relaxed) - : T(::GetCurrentProcess(), relaxed) {} - - // Sets the interception target to the desired address. - void set_target(void* target) { - fake_target_ = target; - } - - protected: - // Overrides Resolver::Init - virtual NTSTATUS Init(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes) { - NTSTATUS ret = STATUS_SUCCESS; - ret = ResolverThunk::Init(target_module, interceptor_module, target_name, - interceptor_name, interceptor_entry_point, - thunk_storage, storage_bytes); - EXPECT_EQ(STATUS_SUCCESS, ret); - - target_ = fake_target_; - ntdll_base_ = ::GetModuleHandle(L"ntdll.dll"); - return ret; - }; - - private: - // Holds the address of the fake target. - void* fake_target_; - - DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest); -}; - -typedef ResolverThunkTest WinXpResolverTest; - -#if !defined(_WIN64) -typedef ResolverThunkTest Win2kResolverTest; -typedef ResolverThunkTest Win8ResolverTest; -typedef ResolverThunkTest Wow64ResolverTest; -typedef ResolverThunkTest Wow64W8ResolverTest; -#endif - -const BYTE kJump32 = 0xE9; - -void CheckJump(void* source, void* target) { -#pragma pack(push) -#pragma pack(1) - struct Code { - BYTE jump; - ULONG delta; - }; -#pragma pack(pop) - -#if defined(_WIN64) - FAIL() << "Running 32-bit codepath"; -#else - Code* patched = reinterpret_cast(source); - EXPECT_EQ(kJump32, patched->jump); - - ULONG source_addr = bit_cast(source); - ULONG target_addr = bit_cast(target); - EXPECT_EQ(target_addr + 19 - source_addr, patched->delta); -#endif -} - -NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, - sandbox::ServiceResolverThunk* resolver) { - HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); - EXPECT_TRUE(NULL != ntdll_base); - - void* target = ::GetProcAddress(ntdll_base, function); - EXPECT_TRUE(NULL != target); - if (NULL == target) - return STATUS_UNSUCCESSFUL; - - BYTE service[50]; - memcpy(service, target, sizeof(service)); - - static_cast(resolver)->set_target(service); - - // Any pointer will do as an interception_entry_point - void* function_entry = resolver; - size_t thunk_size = resolver->GetThunkSize(); - scoped_array thunk(new char[thunk_size]); - size_t used; - - NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, - function_entry, thunk.get(), thunk_size, - &used); - if (NT_SUCCESS(ret)) { - EXPECT_EQ(thunk_size, used); - EXPECT_NE(0, memcmp(service, target, sizeof(service))); - EXPECT_NE(kJump32, service[0]); - - if (relaxed) { - // It's already patched, let's patch again, and simulate a direct patch. - service[0] = kJump32; - ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, - thunk.get(), thunk_size, &used); - CheckJump(service, thunk.get()); - } - } - - return ret; -} - -sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) { -#if defined(_WIN64) - return new WinXpResolverTest(relaxed); -#else - base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); - if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { - if (os_info->version() >= base::win::VERSION_WIN8) - return new Wow64W8ResolverTest(relaxed); - return new Wow64ResolverTest(relaxed); - } - - if (!sandbox::IsXPSP2OrLater()) - return new Win2kResolverTest(relaxed); - - if (os_info->version() >= base::win::VERSION_WIN8) - return new Win8ResolverTest(relaxed); - - return new WinXpResolverTest(relaxed); -#endif -} - -NTSTATUS PatchNtdll(const char* function, bool relaxed) { - sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed); - - NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver); - delete resolver; - return ret; -} - -TEST(ServiceResolverTest, PatchesServices) { - NTSTATUS ret = PatchNtdll("NtClose", false); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); - - ret = PatchNtdll("NtCreateFile", false); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << - ::GetLastError(); - - ret = PatchNtdll("NtCreateMutant", false); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << - ::GetLastError(); - - ret = PatchNtdll("NtMapViewOfSection", false); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << - ::GetLastError(); -} - -TEST(ServiceResolverTest, FailsIfNotService) { -#if !defined(_WIN64) - EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false)); -#endif - - EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false)); -} - -TEST(ServiceResolverTest, PatchesPatchedServices) { -// We don't support "relaxed mode" for Win64 apps. -#if !defined(_WIN64) - NTSTATUS ret = PatchNtdll("NtClose", true); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); - - ret = PatchNtdll("NtCreateFile", true); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << - ::GetLastError(); - - ret = PatchNtdll("NtCreateMutant", true); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << - ::GetLastError(); - - ret = PatchNtdll("NtMapViewOfSection", true); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << - ::GetLastError(); -#endif -} - -TEST(ServiceResolverTest, MultiplePatchedServices) { -// We don't support "relaxed mode" for Win64 apps. -#if !defined(_WIN64) - sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); - NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); - - ret = PatchNtdllWithResolver("NtCreateFile", true, resolver); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << - ::GetLastError(); - - ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << - ::GetLastError(); - - ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver); - EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << - ::GetLastError(); - delete resolver; -#endif -} - -} // namespace diff --git a/sandbox/src/shared_handles.cc b/sandbox/src/shared_handles.cc deleted file mode 100644 index d07f057..0000000 --- a/sandbox/src/shared_handles.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/shared_handles.h" - -namespace sandbox { - -// Note once again the the assumption here is that the shared memory is -// initialized with zeros in the process that calls SetHandle and that -// the process that calls GetHandle 'sees' this memory. - -SharedHandles::SharedHandles() { - shared_.items = NULL; - shared_.max_items = 0; -} - -bool SharedHandles::Init(void* raw_mem, size_t size_bytes) { - if (size_bytes < sizeof(shared_.items[0])) { - // The shared memory is too small! - return false; - } - shared_.items = static_cast(raw_mem); - shared_.max_items = size_bytes / sizeof(shared_.items[0]); - return true; -} - -// Note that an empty slot is marked with a tag == 0 that is why is -// not a valid imput tag -bool SharedHandles::SetHandle(uint32 tag, HANDLE handle) { - if (0 == tag) { - // Invalid tag - return false; - } - // Find empty slot and put the tag and the handle there - SharedItem* empty_slot = FindByTag(0); - if (NULL == empty_slot) { - return false; - } - empty_slot->tag = tag; - empty_slot->item = handle; - return true; -} - -bool SharedHandles::GetHandle(uint32 tag, HANDLE* handle) { - if (0 == tag) { - // Invalid tag - return false; - } - SharedItem* found = FindByTag(tag); - if (NULL == found) { - return false; - } - *handle = found->item; - return true; -} - -SharedHandles::SharedItem* SharedHandles::FindByTag(uint32 tag) { - for (size_t ix = 0; ix != shared_.max_items; ++ix) { - if (tag == shared_.items[ix].tag) { - return &shared_.items[ix]; - } - } - return NULL; -} - -} // namespace sandbox diff --git a/sandbox/src/shared_handles.h b/sandbox/src/shared_handles.h deleted file mode 100644 index 2c76bfb..0000000 --- a/sandbox/src/shared_handles.h +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SHARED_HANDLES_H__ -#define SANDBOX_SRC_SHARED_HANDLES_H__ - -#include "base/basictypes.h" - -#ifndef HANDLE -// We can provide our own windows compatilble handle definition, but -// in general we want to rely on the client of this api to include -// the proper windows headers. Note that we don't want to bring the -// whole into scope if we don't have to. -typedef void* HANDLE; -#endif - -namespace sandbox { - -// SharedHandles is a simple class to stash and find windows object handles -// given a raw block of memory which is shared between two processes. -// It addresses the need to communicate a handle value between two windows -// processes given that they are already sharing some memory. -// -// This class is not exposed directly to users of the sanbox API, instead -// we expose the wrapper methods TargetProcess::TransferHandle( ) and -// TargetServices::GetTransferHandle() -// -// Use it for a small number of items, since internaly uses linear seach -// -// The use is very simple. Given a shared memory between proces A and B: -// process A: -// HANDLE handle = SomeFunction(..); -// SharedHandles shared_handes; -// shared_handles.Init(memory) -// shared_handles.SetHandle(3, handle); -// -// process B: -// SharedHandles shared_handes; -// shared_handles.Init(memory) -// HANDLE handle = shared_handles.GetHandle(3); -// -// Note that '3' in this example is a unique id, that must be agreed before -// transfer -// -// Note2: While this class can be used in a single process, there are -// better alternatives such as STL -// -// Note3: Under windows a kernel object handle in one process does not -// make sense for another process unless there is a DuplicateHandle( ) -// call involved which this class DOES NOT do that for you. -// -// Note4: Under windows, shared memory when created is initialized to -// zeros always. If you are not using shared memory it is your responsability -// to zero it for the setter process and to copy it to the getter process. -class SharedHandles { - public: - SharedHandles(); - - // Initializes the shared memory for use. - // Pass the shared memory base and size. It will internally compute - // how many handles can it store. If initialization fails the return value - // is false. - bool Init(void* raw_mem, size_t size_bytes); - - // Sets a handle in the shared memory for transfer. - // Parameters: - // tag : an integer, different from zero that uniquely identfies the - // handle to transfer. - // handle: the handle value associated with 'tag' to tranfer - // Returns false if there is not enough space in the shared memory for - // this handle. - bool SetHandle(uint32 tag, HANDLE handle); - - // Gets a handle previously stored by SetHandle. - // Parameters: - // tag: an integer different from zero that uniquely identfies the handle - // to retrieve. - // *handle: output handle value if the call was succesful. - // If a handle with the provided tag is not found the return value is false. - // If the tag is found the return value is true. - bool GetHandle(uint32 tag, HANDLE* handle); - - private: - // A single item is the tuple handle/tag - struct SharedItem { - uint32 tag; - void* item; - }; - - // SharedMem is used to layout the memory as an array of SharedItems - struct SharedMem { - size_t max_items; - SharedItem* items; - }; - - // Finds an Item tuple provided the handle tag. - // Uses linear search because we expect the number of handles to be - // small (say less than ~100). - SharedItem* FindByTag(uint32 tag); - - SharedMem shared_; - DISALLOW_COPY_AND_ASSIGN(SharedHandles); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SHARED_HANDLES_H__ diff --git a/sandbox/src/sharedmem_ipc_client.cc b/sandbox/src/sharedmem_ipc_client.cc deleted file mode 100644 index 30c03b9..0000000 --- a/sandbox/src/sharedmem_ipc_client.cc +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/crosscall_params.h" -#include "base/logging.h" - -namespace sandbox { - -// Get the base of the data buffer of the channel; this is where the input -// parameters get serialized. Since they get serialized directly into the -// channel we avoid one copy. -void* SharedMemIPCClient::GetBuffer() { - bool failure = false; - size_t ix = LockFreeChannel(&failure); - if (failure) { - return NULL; - } - return reinterpret_cast(control_) + - control_->channels[ix].channel_base; -} - -// If we need to cancel an IPC before issuing DoCall -// our client should call FreeBuffer with the same pointer -// returned by GetBuffer. -void SharedMemIPCClient::FreeBuffer(void* buffer) { - size_t num = ChannelIndexFromBuffer(buffer); - ChannelControl* channel = control_->channels; - LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel); - DCHECK(kFreeChannel != result); - result; -} - -// The constructor simply casts the shared memory to the internal -// structures. This is a cheap step that is why this IPC object can -// and should be constructed per call. -SharedMemIPCClient::SharedMemIPCClient(void* shared_mem) - : control_(reinterpret_cast(shared_mem)) { - first_base_ = reinterpret_cast(shared_mem) + - control_->channels[0].channel_base; - // There must be at least one channel. - DCHECK(0 != control_->channels_count); -} - -// Do the IPC. At this point the channel should have already been -// filled with the serialized input parameters. -// We follow the pattern explained in the header file. -ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params, - CrossCallReturn* answer) { - if (!control_->server_alive) - return SBOX_ERROR_CHANNEL_ERROR; - - size_t num = ChannelIndexFromBuffer(params->GetBuffer()); - ChannelControl* channel = control_->channels; - // Note that the IPC tag goes outside the buffer as well inside - // the buffer. This should enable the server to prioritize based on - // IPC tags without having to de-serialize the entire message. - channel[num].ipc_tag = params->GetTag(); - - // Wait for the server to service this IPC call. After kIPCWaitTimeOut1 - // we check if the server_alive mutex was abandoned which will indicate - // that the server has died. - - // While the atomic signaling and waiting is not a requirement, it - // is nice because we save a trip to kernel. - DWORD wait = ::SignalObjectAndWait(channel[num].ping_event, - channel[num].pong_event, - kIPCWaitTimeOut1, FALSE); - if (WAIT_TIMEOUT == wait) { - // The server is taking too long. Enter a loop were we check if the - // server_alive mutex has been abandoned which would signal a server crash - // or else we keep waiting for a response. - while (true) { - wait = ::WaitForSingleObject(control_->server_alive, 0); - if (WAIT_TIMEOUT == wait) { - // Server seems still alive. We already signaled so here we just wait. - wait = ::WaitForSingleObject(channel[num].pong_event, kIPCWaitTimeOut1); - if (WAIT_OBJECT_0 == wait) { - // The server took a long time but responded. - break; - } else if (WAIT_TIMEOUT == wait) { - continue; - } else { - return SBOX_ERROR_CHANNEL_ERROR; - } - } else { - // The server has crashed and windows has signaled the mutex as - // abandoned. - ::InterlockedExchange(&channel[num].state, kAbandonnedChannel); - control_->server_alive = 0; - return SBOX_ERROR_CHANNEL_ERROR; - } - } - } else if (WAIT_OBJECT_0 != wait) { - // Probably the server crashed before the kIPCWaitTimeOut1 occurred. - return SBOX_ERROR_CHANNEL_ERROR; - } - - // The server has returned an answer, copy it and free the channel. - memcpy(answer, params->GetCallReturn(), sizeof(CrossCallReturn)); - - // Return the IPC state It can indicate that while the IPC has - // completed some error in the Broker has caused to not return valid - // results. - return answer->call_outcome; -} - -// Locking a channel is a simple as looping over all the channels -// looking for one that is has state = kFreeChannel and atomically -// swapping it to kBusyChannel. -// If there is no free channel, then we must back off so some other -// thread makes progress and frees a channel. To back off we sleep. -size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) { - if (0 == control_->channels_count) { - *severe_failure = true; - return 0; - } - ChannelControl* channel = control_->channels; - do { - for (size_t ix = 0; ix != control_->channels_count; ++ix) { - if (kFreeChannel == ::InterlockedCompareExchange(&channel[ix].state, - kBusyChannel, - kFreeChannel)) { - *severe_failure = false; - return ix; - } - } - // We did not find any available channel, maybe the server is dead. - DWORD wait = ::WaitForSingleObject(control_->server_alive, - kIPCWaitTimeOut2); - if (WAIT_TIMEOUT != wait) { - // The server is dead and we outlive it enough to get in trouble. - *severe_failure = true; - return 0; - } - } - while (true); -} - -// Find out which channel we are from the pointer returned by GetBuffer. -size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) { - ptrdiff_t d = reinterpret_cast(buffer) - first_base_; - size_t num = d/kIPCChannelSize; - DCHECK(num < control_->channels_count); - return (num); -} - -} // namespace sandbox diff --git a/sandbox/src/sharedmem_ipc_client.h b/sandbox/src/sharedmem_ipc_client.h deleted file mode 100644 index 90d0022..0000000 --- a/sandbox/src/sharedmem_ipc_client.h +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__ -#define SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__ - -#include "sandbox/src/crosscall_params.h" -#include "sandbox/src/sandbox.h" - -// IPC transport implementation that uses shared memory. -// This is the client side -// -// The shared memory is divided on blocks called channels, and potentially -// it can perform as many concurrent IPC calls as channels. The IPC over -// each channel is strictly synchronous for the client. -// -// Each channel as a channel control section associated with. Each control -// section has two kernel events (known as ping and pong) and a integer -// variable that maintains a state -// -// this is the state diagram of a channel: -// -// locked in service -// kFreeChannel---------->BusyChannel-------------->kAckChannel -// ^ | -// |_________________________________________________| -// answer ready -// -// The protocol is as follows: -// 1) client finds a free channel: state = kFreeChannel -// 2) does an atomic compare-and-swap, now state = BusyChannel -// 3) client writes the data into the channel buffer -// 4) client signals the ping event and waits (blocks) on the pong event -// 5) eventually the server signals the pong event -// 6) the client awakes and reads the answer from the same channel -// 7) the client updates its InOut parameters with the new data from the -// shared memory section. -// 8) the client atomically sets the state = kFreeChannel -// -// In the shared memory the layout is as follows: -// -// [ channel count ] -// [ channel control 0] -// [ channel control 1] -// [ channel control N] -// [ channel buffer 0 ] 1024 bytes -// [ channel buffer 1 ] 1024 bytes -// [ channel buffer N ] 1024 bytes -// -// By default each channel buffer is 1024 bytes -namespace sandbox { - -// the possible channel states as described above -enum ChannelState { - // channel is free - kFreeChannel = 1, - // IPC in progress client side - kBusyChannel, - // IPC in progress server side - kAckChannel, - // not used right now - kReadyChannel, - // IPC abandoned by client side - kAbandonnedChannel -}; - -// The next two constants control the time outs for the IPC. -const DWORD kIPCWaitTimeOut1 = 1000; // Milliseconds. -const DWORD kIPCWaitTimeOut2 = 50; // Milliseconds. - -// the channel control structure -struct ChannelControl { - // points to be beginning of the channel buffer, where data goes - size_t channel_base; - // maintains the state from the ChannelState enumeration - volatile LONG state; - // the ping event is signaled by the client when the IPC data is ready on - // the buffer - HANDLE ping_event; - // the client waits on the pong event for the IPC answer back - HANDLE pong_event; - // the IPC unique identifier - uint32 ipc_tag; -}; - -struct IPCControl { - // total number of channels available, some might be busy at a given time - size_t channels_count; - // handle to a shared mutex to detect when the server is dead - HANDLE server_alive; - // array of channel control structures - ChannelControl channels[1]; -}; - -// the actual shared memory IPC implementation class. This object is designed -// to be lightweight so it can be constructed on-site (at the calling place) -// wherever an IPC call is needed. -class SharedMemIPCClient { - public: - // Creates the IPC client. - // as parameter it takes the base address of the shared memory - explicit SharedMemIPCClient(void* shared_mem); - - // locks a free channel and returns the channel buffer memory base. This call - // blocks until there is a free channel - void* GetBuffer(); - - // releases the lock on the channel, for other to use. call this if you have - // called GetBuffer and you want to abort but have not called yet DoCall() - void FreeBuffer(void* buffer); - - // Performs the actual IPC call. - // params: The blob of packed input parameters. - // answer: upon IPC completion, it contains the server answer to the IPC. - // If the return value is not SBOX_ERROR_CHANNEL_ERROR, the caller has to free - // the channel. - // returns ALL_OK if the IPC mechanism successfully delivered. You still need - // to check on the answer structure to see the actual IPC result. - ResultCode DoCall(CrossCallParams* params, CrossCallReturn* answer); - - private: - // Returns the index of the first free channel. It sets 'severe_failure' - // to true if there is an unrecoverable error that does not allow to - // find a channel. - size_t LockFreeChannel(bool* severe_failure); - // Return the channel index given the address of the buffer. - size_t ChannelIndexFromBuffer(const void* buffer); - IPCControl* control_; - // point to the first channel base - char* first_base_; -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__ diff --git a/sandbox/src/sharedmem_ipc_server.cc b/sandbox/src/sharedmem_ipc_server.cc deleted file mode 100644 index ba90d1b..0000000 --- a/sandbox/src/sharedmem_ipc_server.cc +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/callback.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "sandbox/src/sharedmem_ipc_server.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/crosscall_params.h" -#include "sandbox/src/crosscall_server.h" - -namespace sandbox { - -SharedMemIPCServer::SharedMemIPCServer(HANDLE target_process, - DWORD target_process_id, - HANDLE target_job, - ThreadProvider* thread_provider, - Dispatcher* dispatcher) - : client_control_(NULL), - thread_provider_(thread_provider), - target_process_(target_process), - target_process_id_(target_process_id), - target_job_object_(target_job), - call_dispatcher_(dispatcher) { -} - -SharedMemIPCServer::~SharedMemIPCServer() { - // Free the wait handles associated with the thread pool. - if (!thread_provider_->UnRegisterWaits(this)) { - // Better to leak than to crash. - return; - } - // Free the IPC signal events. - ServerContexts::iterator it; - for (it = server_contexts_.begin(); it != server_contexts_.end(); ++it) { - ServerControl* context = (*it); - ::CloseHandle(context->ping_event); - ::CloseHandle(context->pong_event); - delete context; - } -} - -bool SharedMemIPCServer::Init(void* shared_mem, uint32 shared_size, - uint32 channel_size) { - // The shared memory needs to be at least as big as a channel. - if (shared_size < channel_size) { - return false; - } - // The channel size should be aligned. - if (0 != (channel_size % 32)) { - return false; - } - - // Calculate how many channels we can fit in the shared memory. - shared_size -= offsetof(IPCControl, channels); - size_t channel_count = shared_size / (sizeof(ChannelControl) + channel_size); - - // If we cannot fit even one channel we bail out. - if (0 == channel_count) { - return false; - } - // Calculate the start of the first channel. - size_t base_start = (sizeof(ChannelControl)* channel_count) + - offsetof(IPCControl, channels); - - client_control_ = reinterpret_cast(shared_mem); - client_control_->channels_count = 0; - - // This is the initialization that we do per-channel. Basically: - // 1) make two events (ping & pong) - // 2) create handles to the events for the client and the server. - // 3) initialize the channel (client_context) with the state. - // 4) initialize the server side of the channel (service_context). - // 5) call the thread provider RegisterWait to register the ping events. - for (size_t ix = 0; ix != channel_count; ++ix) { - ChannelControl* client_context = &client_control_->channels[ix]; - ServerControl* service_context = new ServerControl; - server_contexts_.push_back(service_context); - - if (!MakeEvents(&service_context->ping_event, - &service_context->pong_event, - &client_context->ping_event, - &client_context->pong_event)) { - return false; - } - - client_context->channel_base = base_start; - client_context->state = kFreeChannel; - - // Note that some of these values are available as members of this - // object but we put them again into the service_context because we - // will be called on a static method (ThreadPingEventReady) - service_context->shared_base = reinterpret_cast(shared_mem); - service_context->channel_size = channel_size; - service_context->channel = client_context; - service_context->channel_buffer = service_context->shared_base + - client_context->channel_base; - service_context->dispatcher = call_dispatcher_; - service_context->target_info.process = target_process_; - service_context->target_info.process_id = target_process_id_; - service_context->target_info.job_object = target_job_object_; - // Advance to the next channel. - base_start += channel_size; - // Register the ping event with the threadpool. - thread_provider_->RegisterWait(this, service_context->ping_event, - ThreadPingEventReady, service_context); - } - - // We create a mutex that the server locks. If the server dies unexpectedly, - // the thread that owns it will fail to release the lock and windows will - // report to the target (when it tries to acquire it) that the wait was - // abandoned. Note: We purposely leak the local handle because we want it to - // be closed by Windows itself so it is properly marked as abandoned if the - // server dies. - if (!::DuplicateHandle(::GetCurrentProcess(), - ::CreateMutexW(NULL, TRUE, NULL), - target_process_, &client_control_->server_alive, - SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, 0)) { - return false; - } - // This last setting indicates to the client all is setup. - client_control_->channels_count = channel_count; - return true; -} - -// Releases memory allocated for IPC arguments, if needed. -void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]) { - for (size_t i = 0; i < kMaxIpcParams; i++) { - switch (ipc_params->args[i]) { - case WCHAR_TYPE: { - delete reinterpret_cast(args[i]); - args[i] = NULL; - break; - } - case INOUTPTR_TYPE: { - delete reinterpret_cast(args[i]); - args[i] = NULL; - break; - } - default: break; - } - } -} - -// Fills up the list of arguments (args and ipc_params) for an IPC call. -bool GetArgs(CrossCallParamsEx* params, IPCParams* ipc_params, - void* args[kMaxIpcParams]) { - if (kMaxIpcParams < params->GetParamsCount()) - return false; - - for (uint32 i = 0; i < params->GetParamsCount(); i++) { - uint32 size; - ArgType type; - args[i] = params->GetRawParameter(i, &size, &type); - if (args[i]) { - ipc_params->args[i] = type; - switch (type) { - case WCHAR_TYPE: { - scoped_ptr data(new std::wstring); - if (!params->GetParameterStr(i, data.get())) { - args[i] = 0; - ReleaseArgs(ipc_params, args); - return false; - } - args[i] = data.release(); - break; - } - case ULONG_TYPE: { - uint32 data; - if (!params->GetParameter32(i, &data)) { - ReleaseArgs(ipc_params, args); - return false; - } - IPCInt ipc_int(data); - args[i] = ipc_int.AsVoidPtr(); - break; - } - case VOIDPTR_TYPE : { - void* data; - if (!params->GetParameterVoidPtr(i, &data)) { - ReleaseArgs(ipc_params, args); - return false; - } - args[i] = data; - break; - } - case INOUTPTR_TYPE: { - if (!args[i]) { - ReleaseArgs(ipc_params, args); - return false; - } - CountedBuffer* buffer = new CountedBuffer(args[i] , size); - args[i] = buffer; - break; - } - default: break; - } - } - } - return true; -} - -bool SharedMemIPCServer::InvokeCallback(const ServerControl* service_context, - void* ipc_buffer, - CrossCallReturn* call_result) { - // Set the default error code; - SetCallError(SBOX_ERROR_INVALID_IPC, call_result); - uint32 output_size = 0; - // Parse, verify and copy the message. The handler operates on a copy - // of the message so the client cannot play dirty tricks by changing the - // data in the channel while the IPC is being processed. - scoped_ptr params( - CrossCallParamsEx::CreateFromBuffer(ipc_buffer, - service_context->channel_size, - &output_size)); - if (!params.get()) - return false; - - uint32 tag = params->GetTag(); - COMPILE_ASSERT(0 == INVALID_TYPE, Incorrect_type_enum); - IPCParams ipc_params = {0}; - ipc_params.ipc_tag = tag; - - void* args[kMaxIpcParams]; - if (!GetArgs(params.get(), &ipc_params, args)) - return false; - - IPCInfo ipc_info = {0}; - ipc_info.ipc_tag = tag; - ipc_info.client_info = &service_context->target_info; - Dispatcher* dispatcher = service_context->dispatcher; - DCHECK(dispatcher); - bool error = true; - Dispatcher* handler = NULL; - - Dispatcher::CallbackGeneric callback_generic; - handler = dispatcher->OnMessageReady(&ipc_params, &callback_generic); - if (handler) { - switch (params->GetParamsCount()) { - case 0: { - // Ask the IPC dispatcher if she can service this IPC. - Dispatcher::Callback0 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info)) - break; - error = false; - break; - } - case 1: { - Dispatcher::Callback1 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info, args[0])) - break; - error = false; - break; - } - case 2: { - Dispatcher::Callback2 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info, args[0], args[1])) - break; - error = false; - break; - } - case 3: { - Dispatcher::Callback3 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2])) - break; - error = false; - break; - } - case 4: { - Dispatcher::Callback4 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], - args[3])) - break; - error = false; - break; - } - case 5: { - Dispatcher::Callback5 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], - args[4])) - break; - error = false; - break; - } - case 6: { - Dispatcher::Callback6 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], - args[4], args[5])) - break; - error = false; - break; - } - case 7: { - Dispatcher::Callback7 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], - args[4], args[5], args[6])) - break; - error = false; - break; - } - case 8: { - Dispatcher::Callback8 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], - args[4], args[5], args[6], args[7])) - break; - error = false; - break; - } - case 9: { - Dispatcher::Callback9 callback = - reinterpret_cast(callback_generic); - if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], - args[4], args[5], args[6], args[7], args[8])) - break; - error = false; - break; - } - default: { - NOTREACHED(); - break; - } - } - } - - if (error) { - if (handler) - SetCallError(SBOX_ERROR_FAILED_IPC, call_result); - } else { - memcpy(call_result, &ipc_info.return_info, sizeof(*call_result)); - SetCallSuccess(call_result); - if (params->IsInOut()) { - // Maybe the params got changed by the broker. We need to upadte the - // memory section. - memcpy(ipc_buffer, params.get(), output_size); - } - } - - ReleaseArgs(&ipc_params, args); - - return !error; -} - -// This function gets called by a thread from the thread pool when a -// ping event fires. The context is the same as passed in the RegisterWait() -// call above. -void __stdcall SharedMemIPCServer::ThreadPingEventReady(void* context, - unsigned char) { - if (NULL == context) { - DCHECK(false); - return; - } - ServerControl* service_context = reinterpret_cast(context); - // Since the event fired, the channel *must* be busy. Change to kAckChannel - // while we service it. - LONG last_state = - ::InterlockedCompareExchange(&service_context->channel->state, - kAckChannel, kBusyChannel); - if (kBusyChannel != last_state) { - DCHECK(false); - return; - } - - // Prepare the result structure. At this point we will return some result - // even if the IPC is invalid, malformed or has no handler. - CrossCallReturn call_result = {0}; - void* buffer = service_context->channel_buffer; - - InvokeCallback(service_context, buffer, &call_result); - - // Copy the answer back into the channel and signal the pong event. This - // should wake up the client so he can finish the the ipc cycle. - CrossCallParams* call_params = reinterpret_cast(buffer); - memcpy(call_params->GetCallReturn(), &call_result, sizeof(call_result)); - ::InterlockedExchange(&service_context->channel->state, kAckChannel); - ::SetEvent(service_context->pong_event); -} - -bool SharedMemIPCServer::MakeEvents(HANDLE* server_ping, HANDLE* server_pong, - HANDLE* client_ping, HANDLE* client_pong) { - // Note that the IPC client has no right to delete the events. That would - // cause problems. The server *owns* the events. - const DWORD kDesiredAccess = SYNCHRONIZE | EVENT_MODIFY_STATE; - - // The events are auto reset, and start not signaled. - *server_ping = ::CreateEventW(NULL, FALSE, FALSE, NULL); - if (!::DuplicateHandle(::GetCurrentProcess(), *server_ping, target_process_, - client_ping, kDesiredAccess, FALSE, 0)) { - return false; - } - *server_pong = ::CreateEventW(NULL, FALSE, FALSE, NULL); - if (!::DuplicateHandle(::GetCurrentProcess(), *server_pong, target_process_, - client_pong, kDesiredAccess, FALSE, 0)) { - return false; - } - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/sharedmem_ipc_server.h b/sandbox/src/sharedmem_ipc_server.h deleted file mode 100644 index 7e10174..0000000 --- a/sandbox/src/sharedmem_ipc_server.h +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_ -#define SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_ - -#include - -#include "base/basictypes.h" -#include "base/gtest_prod_util.h" -#include "sandbox/src/crosscall_params.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sharedmem_ipc_client.h" - -// IPC transport implementation that uses shared memory. -// This is the server side -// -// The server side has knowledge about the layout of the shared memory -// and the state transitions. Both are explained in sharedmem_ipc_client.h -// -// As opposed to SharedMemIPClient, the Server object should be one for the -// entire lifetime of the target process. The server is in charge of creating -// the events (ping, pong) both for the client and for the target that are used -// to signal the IPC and also in charge of setting the initial state of the -// channels. -// -// When an IPC is ready, the server relies on being called by on the -// ThreadPingEventReady callback. The IPC server then retrieves the buffer, -// marshals it into a CrossCallParam object and calls the Dispatcher, who is in -// charge of fulfilling the IPC request. -namespace sandbox { - -// the shared memory implementation of the IPC server. There should be one -// of these objects per target (IPC client) process -class SharedMemIPCServer { - public: - // Creates the IPC server. - // target_process: handle to the target process. It must be suspended. - // target_process_id: process id of the target process. - // target_job: the job object handle associated with the target process. - // thread_provider: a thread provider object. - // dispatcher: an object that can service IPC calls. - SharedMemIPCServer(HANDLE target_process, DWORD target_process_id, - HANDLE target_job, ThreadProvider* thread_provider, - Dispatcher* dispatcher); - - ~SharedMemIPCServer(); - - // Initializes the server structures, shared memory structures and - // creates the kernels events used to signal the IPC. - bool Init(void* shared_mem, uint32 shared_size, uint32 channel_size); - - private: - // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes - // do not work with sandbox tests. - FRIEND_TEST_ALL_PREFIXES(IPCTest, SharedMemServerTests); - // When an event fires (IPC request). A thread from the ThreadProvider - // will call this function. The context parameter should be the same as - // provided when ThreadProvider::RegisterWait was called. - static void __stdcall ThreadPingEventReady(void* context, - unsigned char); - - // Makes the client and server events. This function is called once - // per channel. - bool MakeEvents(HANDLE* server_ping, HANDLE* server_pong, - HANDLE* client_ping, HANDLE* client_pong); - - // A copy this structure is maintained per channel. - // Note that a lot of the fields are just the same of what we have in the IPC - // object itself. It is better to have the copies since we can dispatch in the - // static method without worrying about converting back to a member function - // call or about threading issues. - struct ServerControl { - // This channel server ping event. - HANDLE ping_event; - // This channel server pong event. - HANDLE pong_event; - // The size of this channel. - uint32 channel_size; - // The pointer to the actual channel data. - char* channel_buffer; - // The pointer to the base of the shared memory. - char* shared_base; - // A pointer to this channel's client-side control structure this structure - // lives in the shared memory. - ChannelControl* channel; - // the IPC dispatcher associated with this channel. - Dispatcher* dispatcher; - // The target process information associated with this channel. - ClientInfo target_info; - }; - - // Looks for the appropriate handler for this IPC and invokes it. - static bool InvokeCallback(const ServerControl* service_context, - void* ipc_buffer, CrossCallReturn* call_result); - - // Points to the shared memory channel control which lives at - // the start of the shared section. - IPCControl* client_control_; - - // Keeps track of the server side objects that are used to answer an IPC. - typedef std::list ServerContexts; - ServerContexts server_contexts_; - - // The thread provider provides the threads that call back into this object - // when the IPC events fire. - ThreadProvider* thread_provider_; - - // The IPC object is associated with a target process. - HANDLE target_process_; - - // The target process id associated with the IPC object. - DWORD target_process_id_; - - // The target object is inside a job too. - HANDLE target_job_object_; - - // The dispatcher handles 'ready' IPC calls. - Dispatcher* call_dispatcher_; - - DISALLOW_COPY_AND_ASSIGN(SharedMemIPCServer); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_ diff --git a/sandbox/src/sid.cc b/sandbox/src/sid.cc deleted file mode 100644 index 6ed9963..0000000 --- a/sandbox/src/sid.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/sid.h" - -#include "base/logging.h" - -namespace sandbox { - -Sid::Sid(const SID *sid) { - ::CopySid(SECURITY_MAX_SID_SIZE, sid_, const_cast(sid)); -}; - -Sid::Sid(WELL_KNOWN_SID_TYPE type) { - DWORD size_sid = SECURITY_MAX_SID_SIZE; - BOOL result = ::CreateWellKnownSid(type, NULL, sid_, &size_sid); - DCHECK(result); - DBG_UNREFERENCED_LOCAL_VARIABLE(result); -} - -const SID *Sid::GetPSID() const { - return reinterpret_cast(const_cast(sid_)); -} - -} // namespace sandbox diff --git a/sandbox/src/sid.h b/sandbox/src/sid.h deleted file mode 100644 index 4656859..0000000 --- a/sandbox/src/sid.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SID_H_ -#define SANDBOX_SRC_SID_H_ - -#include - -namespace sandbox { - -// This class is used to hold and generate SIDS. -class Sid { - public: - // Constructors initializing the object with the SID passed. - // This is a converting constructor. It is not explicit. - Sid(const SID *sid); - Sid(WELL_KNOWN_SID_TYPE type); - - // Returns sid_. - const SID *GetPSID() const; - - private: - BYTE sid_[SECURITY_MAX_SID_SIZE]; -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SID_H_ diff --git a/sandbox/src/sid_unittest.cc b/sandbox/src/sid_unittest.cc deleted file mode 100644 index f1ac60a..0000000 --- a/sandbox/src/sid_unittest.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains unit tests for the sid class. - -#define _ATL_NO_EXCEPTIONS -#include -#include - -#include "sandbox/src/sid.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -// Calls ::EqualSid. This function exists only to simplify the calls to -// ::EqualSid by removing the need to cast the input params. -BOOL EqualSid(const SID *sid1, const SID *sid2) { - return ::EqualSid(const_cast(sid1), const_cast(sid2)); -} - -// Tests the creation if a Sid -TEST(SidTest, Constructors) { - ATL::CSid sid_world = ATL::Sids::World(); - SID *sid_world_pointer = const_cast(sid_world.GetPSID()); - - // Check the SID* constructor - Sid sid_sid_star(sid_world_pointer); - ASSERT_TRUE(EqualSid(sid_world_pointer, sid_sid_star.GetPSID())); - - // Check the copy constructor - Sid sid_copy(sid_sid_star); - ASSERT_TRUE(EqualSid(sid_world_pointer, sid_copy.GetPSID())); - - // Note that the WELL_KNOWN_SID_TYPE constructor is tested in the GetPSID - // test. -} - -// Tests the method GetPSID -TEST(SidTest, GetPSID) { - // Check for non-null result; - ASSERT_NE(static_cast(NULL), Sid(::WinLocalSid).GetPSID()); - ASSERT_NE(static_cast(NULL), Sid(::WinCreatorOwnerSid).GetPSID()); - ASSERT_NE(static_cast(NULL), Sid(::WinBatchSid).GetPSID()); - - ASSERT_TRUE(EqualSid(Sid(::WinNullSid).GetPSID(), - ATL::Sids::Null().GetPSID())); - - ASSERT_TRUE(EqualSid(Sid(::WinWorldSid).GetPSID(), - ATL::Sids::World().GetPSID())); - - ASSERT_TRUE(EqualSid(Sid(::WinDialupSid).GetPSID(), - ATL::Sids::Dialup().GetPSID())); - - ASSERT_TRUE(EqualSid(Sid(::WinNetworkSid).GetPSID(), - ATL::Sids::Network().GetPSID())); - - ASSERT_TRUE(EqualSid(Sid(::WinBuiltinAdministratorsSid).GetPSID(), - ATL::Sids::Admins().GetPSID())); - - ASSERT_TRUE(EqualSid(Sid(::WinBuiltinUsersSid).GetPSID(), - ATL::Sids::Users().GetPSID())); - - ASSERT_TRUE(EqualSid(Sid(::WinBuiltinGuestsSid).GetPSID(), - ATL::Sids::Guests().GetPSID())); - - ASSERT_TRUE(EqualSid(Sid(::WinProxySid).GetPSID(), - ATL::Sids::Proxy().GetPSID())); -} - -} // namespace sandbox diff --git a/sandbox/src/sidestep/ia32_modrm_map.cpp b/sandbox/src/sidestep/ia32_modrm_map.cpp deleted file mode 100644 index b22ab1d..0000000 --- a/sandbox/src/sidestep/ia32_modrm_map.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Table of relevant information about how to decode the ModR/M byte. -// Based on information in the IA-32 Intel Architecture -// Software Developer's Manual Volume 2: Instruction Set Reference. - -#include "sandbox/src/sidestep/mini_disassembler.h" -#include "sandbox/src/sidestep/mini_disassembler_types.h" - -namespace sidestep { - -const ModrmEntry MiniDisassembler::s_ia16_modrm_map_[] = { -// mod == 00 - /* r/m == 000 */ { false, false, OS_ZERO }, - /* r/m == 001 */ { false, false, OS_ZERO }, - /* r/m == 010 */ { false, false, OS_ZERO }, - /* r/m == 011 */ { false, false, OS_ZERO }, - /* r/m == 100 */ { false, false, OS_ZERO }, - /* r/m == 101 */ { false, false, OS_ZERO }, - /* r/m == 110 */ { true, false, OS_WORD }, - /* r/m == 111 */ { false, false, OS_ZERO }, -// mod == 01 - /* r/m == 000 */ { true, false, OS_BYTE }, - /* r/m == 001 */ { true, false, OS_BYTE }, - /* r/m == 010 */ { true, false, OS_BYTE }, - /* r/m == 011 */ { true, false, OS_BYTE }, - /* r/m == 100 */ { true, false, OS_BYTE }, - /* r/m == 101 */ { true, false, OS_BYTE }, - /* r/m == 110 */ { true, false, OS_BYTE }, - /* r/m == 111 */ { true, false, OS_BYTE }, -// mod == 10 - /* r/m == 000 */ { true, false, OS_WORD }, - /* r/m == 001 */ { true, false, OS_WORD }, - /* r/m == 010 */ { true, false, OS_WORD }, - /* r/m == 011 */ { true, false, OS_WORD }, - /* r/m == 100 */ { true, false, OS_WORD }, - /* r/m == 101 */ { true, false, OS_WORD }, - /* r/m == 110 */ { true, false, OS_WORD }, - /* r/m == 111 */ { true, false, OS_WORD }, -// mod == 11 - /* r/m == 000 */ { false, false, OS_ZERO }, - /* r/m == 001 */ { false, false, OS_ZERO }, - /* r/m == 010 */ { false, false, OS_ZERO }, - /* r/m == 011 */ { false, false, OS_ZERO }, - /* r/m == 100 */ { false, false, OS_ZERO }, - /* r/m == 101 */ { false, false, OS_ZERO }, - /* r/m == 110 */ { false, false, OS_ZERO }, - /* r/m == 111 */ { false, false, OS_ZERO } -}; - -const ModrmEntry MiniDisassembler::s_ia32_modrm_map_[] = { -// mod == 00 - /* r/m == 000 */ { false, false, OS_ZERO }, - /* r/m == 001 */ { false, false, OS_ZERO }, - /* r/m == 010 */ { false, false, OS_ZERO }, - /* r/m == 011 */ { false, false, OS_ZERO }, - /* r/m == 100 */ { false, true, OS_ZERO }, - /* r/m == 101 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 110 */ { false, false, OS_ZERO }, - /* r/m == 111 */ { false, false, OS_ZERO }, -// mod == 01 - /* r/m == 000 */ { true, false, OS_BYTE }, - /* r/m == 001 */ { true, false, OS_BYTE }, - /* r/m == 010 */ { true, false, OS_BYTE }, - /* r/m == 011 */ { true, false, OS_BYTE }, - /* r/m == 100 */ { true, true, OS_BYTE }, - /* r/m == 101 */ { true, false, OS_BYTE }, - /* r/m == 110 */ { true, false, OS_BYTE }, - /* r/m == 111 */ { true, false, OS_BYTE }, -// mod == 10 - /* r/m == 000 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 001 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 010 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 011 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 100 */ { true, true, OS_DOUBLE_WORD }, - /* r/m == 101 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 110 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 111 */ { true, false, OS_DOUBLE_WORD }, -// mod == 11 - /* r/m == 000 */ { false, false, OS_ZERO }, - /* r/m == 001 */ { false, false, OS_ZERO }, - /* r/m == 010 */ { false, false, OS_ZERO }, - /* r/m == 011 */ { false, false, OS_ZERO }, - /* r/m == 100 */ { false, false, OS_ZERO }, - /* r/m == 101 */ { false, false, OS_ZERO }, - /* r/m == 110 */ { false, false, OS_ZERO }, - /* r/m == 111 */ { false, false, OS_ZERO }, -}; - -}; // namespace sidestep diff --git a/sandbox/src/sidestep/ia32_opcode_map.cpp b/sandbox/src/sidestep/ia32_opcode_map.cpp deleted file mode 100644 index 9f37cef..0000000 --- a/sandbox/src/sidestep/ia32_opcode_map.cpp +++ /dev/null @@ -1,1159 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Opcode decoding maps. Based on the IA-32 Intel Architecture -// Software Developer's Manual Volume 2: Instruction Set Reference. Idea -// for how to lay out the tables in memory taken from the implementation -// in the Bastard disassembly environment. - -#include "sandbox/src/sidestep/mini_disassembler.h" - -namespace sidestep { - -/* -* This is the first table to be searched; the first field of each -* Opcode in the table is either 0 to indicate you're in the -* right table, or an index to the correct table, in the global -* map g_pentiumOpcodeMap -*/ -const Opcode s_first_opcode_byte[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF */ { 1, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x10 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x11 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x12 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x13 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x14 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x15 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x16 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x17 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x18 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x19 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1E */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1F */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x20 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x21 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x22 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x23 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x24 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x25 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x26 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x27 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "daa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x28 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x29 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "das", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x30 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x31 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x32 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x33 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x34 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x35 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x36 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x37 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aaa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x38 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x39 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aas", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x40 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x41 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x42 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x43 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x44 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x45 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x46 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x47 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x48 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x49 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x50 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x51 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x52 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x53 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x54 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x55 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x56 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x57 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x58 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x59 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x60 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x61 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x62 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_A, AM_NOT_USED, "bound", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x63 */ { 0, IT_GENERIC, AM_E | OT_W, AM_G | OT_W, AM_NOT_USED, "arpl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x64 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x65 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x66 */ { 0, IT_PREFIX_OPERAND, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x67 */ { 0, IT_PREFIX_ADDRESS, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x68 */ { 0, IT_GENERIC, AM_I | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x69 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_V, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6A */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_B, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6C */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "insb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6D */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "insd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6E */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X | OT_B, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X | OT_V, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x70 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x71 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x72 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x73 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x74 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x75 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x76 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x77 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x78 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x79 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7A */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7B */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7C */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7D */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7E */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7F */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x80 */ { 2, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x81 */ { 3, IT_REFERENCE, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x82 */ { 4, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x83 */ { 5, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x84 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x85 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x86 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x87 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x88 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x89 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8C */ { 0, IT_GENERIC, AM_E | OT_W, AM_S | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8D */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, "lea", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8E */ { 0, IT_GENERIC, AM_S | OT_W, AM_E | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8F */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x90 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "nop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x91 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x92 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x93 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x94 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x95 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x96 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x97 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x98 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cwde", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x99 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cdq", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9A */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "callf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9B */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wait", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9E */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_O | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_O | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA2 */ { 0, IT_GENERIC, AM_O | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA3 */ { 0, IT_GENERIC, AM_O | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA4 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "movsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA5 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "movsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA6 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "cmpsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA7 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "cmpsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAA */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "stosb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAB */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "stosd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X| OT_B, AM_NOT_USED, "lodsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X| OT_V, AM_NOT_USED, "lodsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAE */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_Y | OT_B, AM_NOT_USED, "scasb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_Y | OT_V, AM_NOT_USED, "scasd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB1 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB2 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB3 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB8 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBA */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBB */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBC */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBE */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC0 */ { 6, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC1 */ { 7, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC2 */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC3 */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC4 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "les", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "lds", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC8 */ { 0, IT_GENERIC, AM_I | OT_W, AM_I | OT_B, AM_NOT_USED, "enter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "leave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCA */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCB */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "int3", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCD */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "int", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCE */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "into", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCF */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "iret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD0 */ { 8, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD1 */ { 9, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD2 */ { 10, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD3 */ { 11, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD4 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aam", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD5 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "xlat", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - - // The following 8 lines would be references to the FPU tables, but we currently - // do not support the FPU instructions in this disassembler. - - /* 0xD8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDA */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDB */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDC */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDD */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDE */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDF */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - - - /* 0xE0 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE1 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE2 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE3 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jcxz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE6 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE7 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE8 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE9 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEA */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEB */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xED */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEF */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_V, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF0 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lock:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF2 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "repne:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF3 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rep:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF4 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "hlt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF6 */ { 12, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF7 */ { 13, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cli", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFB */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFD */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "std", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFE */ { 14, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFF */ { 15, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f[] = { - /* 0x0 */ { 16, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 17, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lsl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "invd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wbinvd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud2", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x10 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movups", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "movsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "movss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movupd" } }, - /* 0x11 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movups", true, - /* F2h */ { 0, IT_GENERIC, AM_W | OT_SD, AM_V | OT_SD, AM_NOT_USED, "movsd" }, - /* F3h */ { 0, IT_GENERIC, AM_W | OT_SS, AM_V | OT_SS, AM_NOT_USED, "movss" }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movupd" } }, - /* 0x12 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // only one of ... - /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // ...these two is correct, Intel doesn't specify which - /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_S, AM_NOT_USED, "movlpd" } }, - /* 0x13 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlpd" } }, - /* 0x14 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpcklps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpcklpd" } }, - /* 0x15 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpckhps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpckhpd" } }, - /* 0x16 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // only one of... - /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // ...these two is correct, Intel doesn't specify which - /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhpd" } }, - /* 0x17 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhpd" } }, - /* 0x18 */ { 18, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x19 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1C */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x20 */ { 0, IT_GENERIC, AM_R | OT_D, AM_C | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x21 */ { 0, IT_GENERIC, AM_R | OT_D, AM_D | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x22 */ { 0, IT_GENERIC, AM_C | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x23 */ { 0, IT_GENERIC, AM_D | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x24 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x25 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x26 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x27 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x28 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movaps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movapd" } }, - /* 0x29 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movaps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movapd" } }, - /* 0x2A */ { 0, IT_GENERIC, AM_V | OT_PS, AM_Q | OT_Q, AM_NOT_USED, "cvtpi2ps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_E | OT_D, AM_NOT_USED, "cvtsi2sd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_E | OT_D, AM_NOT_USED, "cvtsi2ss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_Q | OT_DQ, AM_NOT_USED, "cvtpi2pd" } }, - /* 0x2B */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movntps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movntpd" } }, - /* 0x2C */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvttps2pi", true, - /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvttsd2si" }, - /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvttss2si" }, - /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2pi" } }, - /* 0x2D */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvtps2pi", true, - /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvtsd2si" }, - /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvtss2si" }, - /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2pi" } }, - /* 0x2E */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "ucomiss", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "ucomisd" } }, - /* 0x2F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_SS, AM_NOT_USED, "comiss", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "comisd" } }, - /* 0x30 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wrmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x31 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdtsc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x32 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x33 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdpmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x34 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysenter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x35 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysexit", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x36 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x37 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x38 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x39 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x40 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x41 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x42 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x43 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x44 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x45 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x46 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x47 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmova", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x48 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x49 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4A */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4D */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4E */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4F */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x50 */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PS, AM_NOT_USED, "movmskps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PD, AM_NOT_USED, "movmskpd" } }, - /* 0x51 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "sqrtps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "sqrtsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "sqrtss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "sqrtpd" } }, - /* 0x52 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rsqrtps", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rsqrtss" }, - /* 66h */ { 0 } }, - /* 0x53 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rcpps", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rcpss" }, - /* 66h */ { 0 } }, - /* 0x54 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andpd" } }, - /* 0x55 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andnps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andnpd" } }, - /* 0x56 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "orps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "orpd" } }, - /* 0x57 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "xorps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "xorpd" } }, - /* 0x58 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "addps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "addsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "addss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "addpd" } }, - /* 0x59 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "mulps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "mulsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "mulss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "mulpd" } }, - /* 0x5A */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PS, AM_NOT_USED, "cvtps2pd", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "cvtsd2ss" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "cvtss2sd" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PD, AM_NOT_USED, "cvtpd2ps" } }, - /* 0x5B */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2ps", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvttps2dq" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvtps2dq" } }, - /* 0x5C */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "subps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "subsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "subss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "subpd" } }, - /* 0x5D */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "minps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "minsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "minss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "minpd" } }, - /* 0x5E */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "divps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "divsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "divss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "divpd" } }, - /* 0x5F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "maxps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "maxsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "maxss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "maxpd" } }, - /* 0x60 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklbw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklbw" } }, - /* 0x61 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklwd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklwd" } }, - /* 0x62 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckldq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpckldq" } }, - /* 0x63 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packsswb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packsswb" } }, - /* 0x64 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtb" } }, - /* 0x65 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtw" } }, - /* 0x66 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtd" } }, - /* 0x67 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packuswb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packuswb" } }, - /* 0x68 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhbw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhbw" } }, - /* 0x69 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhwd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhwd" } }, - /* 0x6A */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhdq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhdq" } }, - /* 0x6B */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packssdw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "packssdw" } }, - /* 0x6C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } }, - /* 0x6D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } }, - /* 0x6E */ { 0, IT_GENERIC, AM_P | OT_D, AM_E | OT_D, AM_NOT_USED, "movd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_NOT_USED, "movd" } }, - /* 0x6F */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "movq", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqu" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqa" } }, - /* 0x70 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_I | OT_B, "pshuf", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshuflw" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufhw" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufd" } }, - /* 0x71 */ { 19, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x72 */ { 20, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x73 */ { 21, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x74 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqb" } }, - /* 0x75 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqw" } }, - /* 0x76 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqd" } }, - /* 0x77 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "emms", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - - // The following six opcodes are escapes into the MMX stuff, which this disassembler does not support. - /* 0x78 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x79 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7A */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7B */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7C */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7D */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - - /* 0x7E */ { 0, IT_GENERIC, AM_E | OT_D, AM_P | OT_D, AM_NOT_USED, "movd", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movq" }, - /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_DQ, AM_NOT_USED, "movd" } }, - /* 0x7F */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_P | OT_Q, AM_NOT_USED, "movq", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqu" }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqa" } }, - /* 0x80 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x81 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x82 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x83 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x84 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x85 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x86 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x87 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x88 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x89 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8A */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8B */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8C */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8D */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8E */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8F */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x90 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seto", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x91 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x92 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x93 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x94 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x95 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x96 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x97 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seta", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x98 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "sets", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x99 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9A */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9B */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9C */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9D */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9E */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9F */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cpuid", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA6 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA7 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rsm", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAC */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAD */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAE */ { 22, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB2 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lss", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB4 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lfs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB5 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lgs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB6 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB7 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud1", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBA */ { 23, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBC */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBD */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBE */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC2 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "cmpps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_I | OT_B, "cmpsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_I | OT_B, "cmpss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "cmppd" } }, - /* 0xC3 */ { 0, IT_GENERIC, AM_E | OT_D, AM_G | OT_D, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_E | OT_D, AM_I | OT_B, "pinsrw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_I | OT_B, "pinsrw" } }, - /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_I | OT_B, "pextrw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_I | OT_B, "pextrw" } }, - /* 0xC6 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "shufps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "shufpd" } }, - /* 0xC7 */ { 24, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC8 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC9 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCA */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCB */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCC */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCD */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCE */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCF */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlw" } }, - /* 0xD2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrld", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrld" } }, - /* 0xD3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlq" } }, - /* 0xD4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddq" } }, - /* 0xD5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmullw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmullw" } }, - /* 0xD6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "unused without prefix", true, - /* F2h */ { 0, IT_GENERIC, AM_P | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movdq2q" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_Q | OT_Q, AM_NOT_USED, "movq2dq" }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movq" } }, - /* 0xD7 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_NOT_USED, "pmovmskb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_NOT_USED, "pmovmskb" } }, - /* 0xD8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusb" } }, - /* 0xD9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusw" } }, - /* 0xDA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminub", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminub" } }, - /* 0xDB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pand", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pand" } }, - /* 0xDC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusb" } }, - /* 0xDD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusw" } }, - /* 0xDE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxub", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxub" } }, - /* 0xDF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pandn", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pandn" } }, - /* 0xE0 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgb" } }, - /* 0xE1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psraw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrqw" } }, - /* 0xE2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrad", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrad" } }, - /* 0xE3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgw" } }, - /* 0xE4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhuw" } }, - /* 0xE5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhw" } }, - /* 0xE6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2dq" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2pd" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2dq" } }, - /* 0xE7 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movntq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movntdq" } }, - /* 0xE8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsb" } }, - /* 0xE9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsw" } }, - /* 0xEA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminsw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminsw" } }, - /* 0xEB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "por", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "por" } }, - /* 0xEC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsb" } }, - /* 0xED */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsw" } }, - /* 0xEE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxsw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxsw" } }, - /* 0xEF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pxor", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pxor" } }, - /* 0xF0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllw" } }, - /* 0xF2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pslld", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pslld" } }, - /* 0xF3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllq" } }, - /* 0xF4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmuludq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmuludq" } }, - /* 0xF5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaddwd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaddwd" } }, - /* 0xF6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psadbw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psadbw" } }, - /* 0xF7 */ { 0, IT_GENERIC, AM_P | OT_PI, AM_Q | OT_PI, AM_NOT_USED, "maskmovq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "maskmovdqu" } }, - /* 0xF8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubb" } }, - /* 0xF9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubw" } }, - /* 0xFA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubd" } }, - /* 0xFB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubq" } }, - /* 0xFC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddb" } }, - /* 0xFD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddw" } }, - /* 0xFE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddd" } }, - /* 0xFF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f00[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "sldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "str", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "ltr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f01[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "smsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lmsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_M | OT_B, AM_NOT_USED, AM_NOT_USED, "invlpg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f18[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f71[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlw" } }, - /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psraw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psraw" } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllw" } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f72[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrld", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrld" } }, - /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrad", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrad" } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "pslld", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslld" } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f73[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlq" } }, - /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllq" } }, - /* 0x7 */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq" } }, -}; - -const Opcode s_opcode_byte_after_0fae[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxsave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxrstor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ldmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "mfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clflush/sfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, -}; - -const Opcode s_opcode_byte_after_0fba[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0fc7[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_Q, AM_NOT_USED, AM_NOT_USED, "cmpxch8b", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_80[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_81[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_82[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_83[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_c0[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_c1[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_d0[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_d1[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_d2[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_d3[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_f6[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_f7[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_fe[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_ff[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -/* -* A table of all the other tables, containing some extra information, e.g. -* how to mask out the byte we're looking at. -*/ -const OpcodeTable MiniDisassembler::s_ia32_opcode_map_[]={ - // One-byte opcodes and jumps to larger - /* 0 */ {s_first_opcode_byte, 0, 0xff, 0, 0xff}, - // Two-byte opcodes (second byte) - /* 1 */ {s_opcode_byte_after_0f, 0, 0xff, 0, 0xff}, - // Start of tables for opcodes using ModR/M bits as extension - /* 2 */ {s_opcode_byte_after_80, 3, 0x07, 0, 0x07}, - /* 3 */ {s_opcode_byte_after_81, 3, 0x07, 0, 0x07}, - /* 4 */ {s_opcode_byte_after_82, 3, 0x07, 0, 0x07}, - /* 5 */ {s_opcode_byte_after_83, 3, 0x07, 0, 0x07}, - /* 6 */ {s_opcode_byte_after_c0, 3, 0x07, 0, 0x07}, - /* 7 */ {s_opcode_byte_after_c1, 3, 0x07, 0, 0x07}, - /* 8 */ {s_opcode_byte_after_d0, 3, 0x07, 0, 0x07}, - /* 9 */ {s_opcode_byte_after_d1, 3, 0x07, 0, 0x07}, - /* 10 */ {s_opcode_byte_after_d2, 3, 0x07, 0, 0x07}, - /* 11 */ {s_opcode_byte_after_d3, 3, 0x07, 0, 0x07}, - /* 12 */ {s_opcode_byte_after_f6, 3, 0x07, 0, 0x07}, - /* 13 */ {s_opcode_byte_after_f7, 3, 0x07, 0, 0x07}, - /* 14 */ {s_opcode_byte_after_fe, 3, 0x07, 0, 0x01}, - /* 15 */ {s_opcode_byte_after_ff, 3, 0x07, 0, 0x07}, - /* 16 */ {s_opcode_byte_after_0f00, 3, 0x07, 0, 0x07}, - /* 17 */ {s_opcode_byte_after_0f01, 3, 0x07, 0, 0x07}, - /* 18 */ {s_opcode_byte_after_0f18, 3, 0x07, 0, 0x07}, - /* 19 */ {s_opcode_byte_after_0f71, 3, 0x07, 0, 0x07}, - /* 20 */ {s_opcode_byte_after_0f72, 3, 0x07, 0, 0x07}, - /* 21 */ {s_opcode_byte_after_0f73, 3, 0x07, 0, 0x07}, - /* 22 */ {s_opcode_byte_after_0fae, 3, 0x07, 0, 0x07}, - /* 23 */ {s_opcode_byte_after_0fba, 3, 0x07, 0, 0x07}, - /* 24 */ {s_opcode_byte_after_0fc7, 3, 0x07, 0, 0x01} -}; - -}; // namespace sidestep diff --git a/sandbox/src/sidestep/mini_disassembler.cpp b/sandbox/src/sidestep/mini_disassembler.cpp deleted file mode 100644 index 514522a..0000000 --- a/sandbox/src/sidestep/mini_disassembler.cpp +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Implementation of MiniDisassembler. - -#ifdef _WIN64 -#error The code in this file should not be used on 64-bit Windows. -#endif - -#include "sandbox/src/sidestep/mini_disassembler.h" - -namespace sidestep { - -MiniDisassembler::MiniDisassembler(bool operand_default_is_32_bits, - bool address_default_is_32_bits) - : operand_default_is_32_bits_(operand_default_is_32_bits), - address_default_is_32_bits_(address_default_is_32_bits) { - Initialize(); -} - -MiniDisassembler::MiniDisassembler() - : operand_default_is_32_bits_(true), - address_default_is_32_bits_(true) { - Initialize(); -} - -InstructionType MiniDisassembler::Disassemble( - unsigned char* start_byte, - unsigned int* instruction_bytes) { - // Clean up any state from previous invocations. - Initialize(); - - // Start by processing any prefixes. - unsigned char* current_byte = start_byte; - unsigned int size = 0; - InstructionType instruction_type = ProcessPrefixes(current_byte, &size); - - if (IT_UNKNOWN == instruction_type) - return instruction_type; - - current_byte += size; - size = 0; - - // Invariant: We have stripped all prefixes, and the operand_is_32_bits_ - // and address_is_32_bits_ flags are correctly set. - - instruction_type = ProcessOpcode(current_byte, 0, &size); - - // Check for error processing instruction - if ((IT_UNKNOWN == instruction_type_) || (IT_UNUSED == instruction_type_)) { - return IT_UNKNOWN; - } - - current_byte += size; - - // Invariant: operand_bytes_ indicates the total size of operands - // specified by the opcode and/or ModR/M byte and/or SIB byte. - // pCurrentByte points to the first byte after the ModR/M byte, or after - // the SIB byte if it is present (i.e. the first byte of any operands - // encoded in the instruction). - - // We get the total length of any prefixes, the opcode, and the ModR/M and - // SIB bytes if present, by taking the difference of the original starting - // address and the current byte (which points to the first byte of the - // operands if present, or to the first byte of the next instruction if - // they are not). Adding the count of bytes in the operands encoded in - // the instruction gives us the full length of the instruction in bytes. - *instruction_bytes += operand_bytes_ + (current_byte - start_byte); - - // Return the instruction type, which was set by ProcessOpcode(). - return instruction_type_; -} - -void MiniDisassembler::Initialize() { - operand_is_32_bits_ = operand_default_is_32_bits_; - address_is_32_bits_ = address_default_is_32_bits_; - operand_bytes_ = 0; - have_modrm_ = false; - should_decode_modrm_ = false; - instruction_type_ = IT_UNKNOWN; - got_f2_prefix_ = false; - got_f3_prefix_ = false; - got_66_prefix_ = false; -} - -InstructionType MiniDisassembler::ProcessPrefixes(unsigned char* start_byte, - unsigned int* size) { - InstructionType instruction_type = IT_GENERIC; - const Opcode& opcode = s_ia32_opcode_map_[0].table_[*start_byte]; - - switch (opcode.type_) { - case IT_PREFIX_ADDRESS: - address_is_32_bits_ = !address_default_is_32_bits_; - goto nochangeoperand; - case IT_PREFIX_OPERAND: - operand_is_32_bits_ = !operand_default_is_32_bits_; - nochangeoperand: - case IT_PREFIX: - - if (0xF2 == (*start_byte)) - got_f2_prefix_ = true; - else if (0xF3 == (*start_byte)) - got_f3_prefix_ = true; - else if (0x66 == (*start_byte)) - got_66_prefix_ = true; - - instruction_type = opcode.type_; - (*size)++; - // we got a prefix, so add one and check next byte - ProcessPrefixes(start_byte + 1, size); - default: - break; // not a prefix byte - } - - return instruction_type; -} - -InstructionType MiniDisassembler::ProcessOpcode(unsigned char* start_byte, - unsigned int table_index, - unsigned int* size) { - const OpcodeTable& table = s_ia32_opcode_map_[table_index]; // Get our table - unsigned char current_byte = (*start_byte) >> table.shift_; - current_byte = current_byte & table.mask_; // Mask out the bits we will use - - // Check whether the byte we have is inside the table we have. - if (current_byte < table.min_lim_ || current_byte > table.max_lim_) { - instruction_type_ = IT_UNKNOWN; - return instruction_type_; - } - - const Opcode& opcode = table.table_[current_byte]; - if (IT_UNUSED == opcode.type_) { - // This instruction is not used by the IA-32 ISA, so we indicate - // this to the user. Probably means that we were pointed to - // a byte in memory that was not the start of an instruction. - instruction_type_ = IT_UNUSED; - return instruction_type_; - } else if (IT_REFERENCE == opcode.type_) { - // We are looking at an opcode that has more bytes (or is continued - // in the ModR/M byte). Recursively find the opcode definition in - // the table for the opcode's next byte. - (*size)++; - ProcessOpcode(start_byte + 1, opcode.table_index_, size); - return instruction_type_; - } - - const SpecificOpcode* specific_opcode = reinterpret_cast< - const SpecificOpcode*>(&opcode); - if (opcode.is_prefix_dependent_) { - if (got_f2_prefix_ && opcode.opcode_if_f2_prefix_.mnemonic_ != 0) { - specific_opcode = &opcode.opcode_if_f2_prefix_; - } else if (got_f3_prefix_ && opcode.opcode_if_f3_prefix_.mnemonic_ != 0) { - specific_opcode = &opcode.opcode_if_f3_prefix_; - } else if (got_66_prefix_ && opcode.opcode_if_66_prefix_.mnemonic_ != 0) { - specific_opcode = &opcode.opcode_if_66_prefix_; - } - } - - // Inv: The opcode type is known. - instruction_type_ = specific_opcode->type_; - - // Let's process the operand types to see if we have any immediate - // operands, and/or a ModR/M byte. - - ProcessOperand(specific_opcode->flag_dest_); - ProcessOperand(specific_opcode->flag_source_); - ProcessOperand(specific_opcode->flag_aux_); - - // Inv: We have processed the opcode and incremented operand_bytes_ - // by the number of bytes of any operands specified by the opcode - // that are stored in the instruction (not registers etc.). Now - // we need to return the total number of bytes for the opcode and - // for the ModR/M or SIB bytes if they are present. - - if (table.mask_ != 0xff) { - if (have_modrm_) { - // we're looking at a ModR/M byte so we're not going to - // count that into the opcode size - ProcessModrm(start_byte, size); - return IT_GENERIC; - } else { - // need to count the ModR/M byte even if it's just being - // used for opcode extension - (*size)++; - return IT_GENERIC; - } - } else { - if (have_modrm_) { - // The ModR/M byte is the next byte. - (*size)++; - ProcessModrm(start_byte + 1, size); - return IT_GENERIC; - } else { - (*size)++; - return IT_GENERIC; - } - } -} - -bool MiniDisassembler::ProcessOperand(int flag_operand) { - bool succeeded = true; - if (AM_NOT_USED == flag_operand) - return succeeded; - - // Decide what to do based on the addressing mode. - switch (flag_operand & AM_MASK) { - // No ModR/M byte indicated by these addressing modes, and no - // additional (e.g. immediate) parameters. - case AM_A: // Direct address - case AM_F: // EFLAGS register - case AM_X: // Memory addressed by the DS:SI register pair - case AM_Y: // Memory addressed by the ES:DI register pair - case AM_IMPLICIT: // Parameter is implicit, occupies no space in - // instruction - break; - - // There is a ModR/M byte but it does not necessarily need - // to be decoded. - case AM_C: // reg field of ModR/M selects a control register - case AM_D: // reg field of ModR/M selects a debug register - case AM_G: // reg field of ModR/M selects a general register - case AM_P: // reg field of ModR/M selects an MMX register - case AM_R: // mod field of ModR/M may refer only to a general register - case AM_S: // reg field of ModR/M selects a segment register - case AM_T: // reg field of ModR/M selects a test register - case AM_V: // reg field of ModR/M selects a 128-bit XMM register - have_modrm_ = true; - break; - - // In these addressing modes, there is a ModR/M byte and it needs to be - // decoded. No other (e.g. immediate) params than indicated in ModR/M. - case AM_E: // Operand is either a general-purpose register or memory, - // specified by ModR/M byte - case AM_M: // ModR/M byte will refer only to memory - case AM_Q: // Operand is either an MMX register or memory (complex - // evaluation), specified by ModR/M byte - case AM_W: // Operand is either a 128-bit XMM register or memory (complex - // eval), specified by ModR/M byte - have_modrm_ = true; - should_decode_modrm_ = true; - break; - - // These addressing modes specify an immediate or an offset value - // directly, so we need to look at the operand type to see how many - // bytes. - case AM_I: // Immediate data. - case AM_J: // Jump to offset. - case AM_O: // Operand is at offset. - switch (flag_operand & OT_MASK) { - case OT_B: // Byte regardless of operand-size attribute. - operand_bytes_ += OS_BYTE; - break; - case OT_C: // Byte or word, depending on operand-size attribute. - if (operand_is_32_bits_) - operand_bytes_ += OS_WORD; - else - operand_bytes_ += OS_BYTE; - break; - case OT_D: // Doubleword, regardless of operand-size attribute. - operand_bytes_ += OS_DOUBLE_WORD; - break; - case OT_DQ: // Double-quadword, regardless of operand-size attribute. - operand_bytes_ += OS_DOUBLE_QUAD_WORD; - break; - case OT_P: // 32-bit or 48-bit pointer, depending on operand-size - // attribute. - if (operand_is_32_bits_) - operand_bytes_ += OS_48_BIT_POINTER; - else - operand_bytes_ += OS_32_BIT_POINTER; - break; - case OT_PS: // 128-bit packed single-precision floating-point data. - operand_bytes_ += OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING; - break; - case OT_Q: // Quadword, regardless of operand-size attribute. - operand_bytes_ += OS_QUAD_WORD; - break; - case OT_S: // 6-byte pseudo-descriptor. - operand_bytes_ += OS_PSEUDO_DESCRIPTOR; - break; - case OT_SD: // Scalar Double-Precision Floating-Point Value - case OT_PD: // Unaligned packed double-precision floating point value - operand_bytes_ += OS_DOUBLE_PRECISION_FLOATING; - break; - case OT_SS: - // Scalar element of a 128-bit packed single-precision - // floating data. - // We simply return enItUnknown since we don't have to support - // floating point - succeeded = false; - break; - case OT_V: // Word or doubleword, depending on operand-size attribute. - if (operand_is_32_bits_) - operand_bytes_ += OS_DOUBLE_WORD; - else - operand_bytes_ += OS_WORD; - break; - case OT_W: // Word, regardless of operand-size attribute. - operand_bytes_ += OS_WORD; - break; - - // Can safely ignore these. - case OT_A: // Two one-word operands in memory or two double-word - // operands in memory - case OT_PI: // Quadword MMX technology register (e.g. mm0) - case OT_SI: // Doubleword integer register (e.g., eax) - break; - - default: - break; - } - break; - - default: - break; - } - - return succeeded; -} - -bool MiniDisassembler::ProcessModrm(unsigned char* start_byte, - unsigned int* size) { - // If we don't need to decode, we just return the size of the ModR/M - // byte (there is never a SIB byte in this case). - if (!should_decode_modrm_) { - (*size)++; - return true; - } - - // We never care about the reg field, only the combination of the mod - // and r/m fields, so let's start by packing those fields together into - // 5 bits. - unsigned char modrm = (*start_byte); - unsigned char mod = modrm & 0xC0; // mask out top two bits to get mod field - modrm = modrm & 0x07; // mask out bottom 3 bits to get r/m field - mod = mod >> 3; // shift the mod field to the right place - modrm = mod | modrm; // combine the r/m and mod fields as discussed - mod = mod >> 3; // shift the mod field to bits 2..0 - - // Invariant: modrm contains the mod field in bits 4..3 and the r/m field - // in bits 2..0, and mod contains the mod field in bits 2..0 - - const ModrmEntry* modrm_entry = 0; - if (address_is_32_bits_) - modrm_entry = &s_ia32_modrm_map_[modrm]; - else - modrm_entry = &s_ia16_modrm_map_[modrm]; - - // Invariant: modrm_entry points to information that we need to decode - // the ModR/M byte. - - // Add to the count of operand bytes, if the ModR/M byte indicates - // that some operands are encoded in the instruction. - if (modrm_entry->is_encoded_in_instruction_) - operand_bytes_ += modrm_entry->operand_size_; - - // Process the SIB byte if necessary, and return the count - // of ModR/M and SIB bytes. - if (modrm_entry->use_sib_byte_) { - (*size)++; - return ProcessSib(start_byte + 1, mod, size); - } else { - (*size)++; - return true; - } -} - -bool MiniDisassembler::ProcessSib(unsigned char* start_byte, - unsigned char mod, - unsigned int* size) { - // get the mod field from the 2..0 bits of the SIB byte - unsigned char sib_base = (*start_byte) & 0x07; - if (0x05 == sib_base) { - switch (mod) { - case 0x00: // mod == 00 - case 0x02: // mod == 10 - operand_bytes_ += OS_DOUBLE_WORD; - break; - case 0x01: // mod == 01 - operand_bytes_ += OS_BYTE; - break; - case 0x03: // mod == 11 - // According to the IA-32 docs, there does not seem to be a disp - // value for this value of mod - default: - break; - } - } - - (*size)++; - return true; -} - -}; // namespace sidestep diff --git a/sandbox/src/sidestep/mini_disassembler.h b/sandbox/src/sidestep/mini_disassembler.h deleted file mode 100644 index 444df36..0000000 --- a/sandbox/src/sidestep/mini_disassembler.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Definition of MiniDisassembler. - -#ifndef SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_H__ -#define SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_H__ - -#include "sandbox/src/sidestep/mini_disassembler_types.h" - -namespace sidestep { - -// This small disassembler is very limited -// in its functionality, and in fact does only the bare minimum required by the -// preamble patching utility. It may be useful for other purposes, however. -// -// The limitations include at least the following: -// -# No support for coprocessor opcodes, MMX, etc. -// -# No machine-readable identification of opcodes or decoding of -// assembly parameters. The name of the opcode (as a string) is given, -// however, to aid debugging. -// -// You may ask what this little disassembler actually does, then? The answer is -// that it does the following, which is exactly what the patching utility needs: -// -# Indicates if opcode is a jump (any kind) or a return (any kind) -// because this is important for the patching utility to determine if -// a function is too short or there are jumps too early in it for it -// to be preamble patched. -// -# The opcode length is always calculated, so that the patching utility -// can figure out where the next instruction starts, and whether it -// already has enough instructions to replace with the absolute jump -// to the patching code. -// -// The usage is quite simple; just create a MiniDisassembler and use its -// Disassemble() method. -// -// If you would like to extend this disassembler, please refer to the -// IA-32 Intel Architecture Software Developer's Manual Volume 2: -// Instruction Set Reference for information about operand decoding -// etc. -class MiniDisassembler { - public: - - // Creates a new instance and sets defaults. - // - // operand_default_32_bits: If true, the default operand size is - // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits. - // address_default_32_bits: If true, the default address size is - // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits. - MiniDisassembler(bool operand_default_32_bits, - bool address_default_32_bits); - - // Equivalent to MiniDisassembler(true, true); - MiniDisassembler(); - - // Attempts to disassemble a single instruction starting from the - // address in memory it is pointed to. - // - // start: Address where disassembly should start. - // instruction_bytes: Variable that will be incremented by - // the length in bytes of the instruction. - // Returns enItJump, enItReturn or enItGeneric on success. enItUnknown - // if unable to disassemble, enItUnused if this seems to be an unused - // opcode. In the last two (error) cases, cbInstruction will be set - // to 0xffffffff. - // - // Postcondition: This instance of the disassembler is ready to be used again, - // with unchanged defaults from creation time. - InstructionType Disassemble(unsigned char* start, - unsigned int* instruction_bytes); - - private: - - // Makes the disassembler ready for reuse. - void Initialize(); - - // Sets the flags for address and operand sizes. - // Returns Number of prefix bytes. - InstructionType ProcessPrefixes(unsigned char* start, unsigned int* size); - - // Sets the flag for whether we have ModR/M, and increments - // operand_bytes_ if any are specifies by the opcode directly. - // Returns Number of opcode bytes. - InstructionType ProcessOpcode(unsigned char* start, - unsigned int table, - unsigned int* size); - - // Checks the type of the supplied operand. Increments - // operand_bytes_ if it directly indicates an immediate etc. - // operand. Asserts have_modrm_ if the operand specifies - // a ModR/M byte. - bool ProcessOperand(int flag_operand); - - // Increments operand_bytes_ by size specified by ModR/M and - // by SIB if present. - // Returns 0 in case of error, 1 if there is just a ModR/M byte, - // 2 if there is a ModR/M byte and a SIB byte. - bool ProcessModrm(unsigned char* start, unsigned int* size); - - // Processes the SIB byte that it is pointed to. - // start: Pointer to the SIB byte. - // mod: The mod field from the ModR/M byte. - // Returns 1 to indicate success (indicates 1 SIB byte) - bool ProcessSib(unsigned char* start, unsigned char mod, unsigned int* size); - - // The instruction type we have decoded from the opcode. - InstructionType instruction_type_; - - // Counts the number of bytes that is occupied by operands in - // the current instruction (note: we don't care about how large - // operands stored in registers etc. are). - unsigned int operand_bytes_; - - // True iff there is a ModR/M byte in this instruction. - bool have_modrm_; - - // True iff we need to decode the ModR/M byte (sometimes it just - // points to a register, we can tell by the addressing mode). - bool should_decode_modrm_; - - // Current operand size is 32 bits if true, 16 bits if false. - bool operand_is_32_bits_; - - // Default operand size is 32 bits if true, 16 bits if false. - bool operand_default_is_32_bits_; - - // Current address size is 32 bits if true, 16 bits if false. - bool address_is_32_bits_; - - // Default address size is 32 bits if true, 16 bits if false. - bool address_default_is_32_bits_; - - // Huge big opcode table based on the IA-32 manual, defined - // in Ia32OpcodeMap.cpp - static const OpcodeTable s_ia32_opcode_map_[]; - - // Somewhat smaller table to help with decoding ModR/M bytes - // when 16-bit addressing mode is being used. Defined in - // Ia32ModrmMap.cpp - static const ModrmEntry s_ia16_modrm_map_[]; - - // Somewhat smaller table to help with decoding ModR/M bytes - // when 32-bit addressing mode is being used. Defined in - // Ia32ModrmMap.cpp - static const ModrmEntry s_ia32_modrm_map_[]; - - // Indicators of whether we got certain prefixes that certain - // silly Intel instructions depend on in nonstandard ways for - // their behaviors. - bool got_f2_prefix_, got_f3_prefix_, got_66_prefix_; -}; - -}; // namespace sidestep - -#endif // SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_H__ diff --git a/sandbox/src/sidestep/mini_disassembler_types.h b/sandbox/src/sidestep/mini_disassembler_types.h deleted file mode 100644 index 1c10626..0000000 --- a/sandbox/src/sidestep/mini_disassembler_types.h +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Several simple types used by the disassembler and some of the patching -// mechanisms. - -#ifndef SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H__ -#define SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H__ - -namespace sidestep { - -// Categories of instructions that we care about -enum InstructionType { - // This opcode is not used - IT_UNUSED, - // This disassembler does not recognize this opcode (error) - IT_UNKNOWN, - // This is not an instruction but a reference to another table - IT_REFERENCE, - // This byte is a prefix byte that we can ignore - IT_PREFIX, - // This is a prefix byte that switches to the nondefault address size - IT_PREFIX_ADDRESS, - // This is a prefix byte that switches to the nondefault operand size - IT_PREFIX_OPERAND, - // A jump or call instruction - IT_JUMP, - // A return instruction - IT_RETURN, - // Any other type of instruction (in this case we don't care what it is) - IT_GENERIC, -}; - -// Lists IA-32 operand sizes in multiples of 8 bits -enum OperandSize { - OS_ZERO = 0, - OS_BYTE = 1, - OS_WORD = 2, - OS_DOUBLE_WORD = 4, - OS_QUAD_WORD = 8, - OS_DOUBLE_QUAD_WORD = 16, - OS_32_BIT_POINTER = 32/8, - OS_48_BIT_POINTER = 48/8, - OS_SINGLE_PRECISION_FLOATING = 32/8, - OS_DOUBLE_PRECISION_FLOATING = 64/8, - OS_DOUBLE_EXTENDED_PRECISION_FLOATING = 80/8, - OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING = 128/8, - OS_PSEUDO_DESCRIPTOR = 6 -}; - -// Operand addressing methods from the IA-32 manual. The enAmMask value -// is a mask for the rest. The other enumeration values are named for the -// names given to the addressing methods in the manual, e.g. enAm_D is for -// the D addressing method. -// -// The reason we use a full 4 bytes and a mask, is that we need to combine -// these flags with the enOperandType to store the details -// on the operand in a single integer. -enum AddressingMethod { - AM_NOT_USED = 0, // This operand is not used for this instruction - AM_MASK = 0x00FF0000, // Mask for the rest of the values in this enumeration - AM_A = 0x00010000, // A addressing type - AM_C = 0x00020000, // C addressing type - AM_D = 0x00030000, // D addressing type - AM_E = 0x00040000, // E addressing type - AM_F = 0x00050000, // F addressing type - AM_G = 0x00060000, // G addressing type - AM_I = 0x00070000, // I addressing type - AM_J = 0x00080000, // J addressing type - AM_M = 0x00090000, // M addressing type - AM_O = 0x000A0000, // O addressing type - AM_P = 0x000B0000, // P addressing type - AM_Q = 0x000C0000, // Q addressing type - AM_R = 0x000D0000, // R addressing type - AM_S = 0x000E0000, // S addressing type - AM_T = 0x000F0000, // T addressing type - AM_V = 0x00100000, // V addressing type - AM_W = 0x00110000, // W addressing type - AM_X = 0x00120000, // X addressing type - AM_Y = 0x00130000, // Y addressing type - AM_REGISTER = 0x00140000, // Specific register is always used as this op - AM_IMPLICIT = 0x00150000, // An implicit, fixed value is used -}; - -// Operand types from the IA-32 manual. The enOtMask value is -// a mask for the rest. The rest of the values are named for the -// names given to these operand types in the manual, e.g. enOt_ps -// is for the ps operand type in the manual. -// -// The reason we use a full 4 bytes and a mask, is that we need -// to combine these flags with the enAddressingMethod to store the details -// on the operand in a single integer. -enum OperandType { - OT_MASK = 0xFF000000, - OT_A = 0x01000000, - OT_B = 0x02000000, - OT_C = 0x03000000, - OT_D = 0x04000000, - OT_DQ = 0x05000000, - OT_P = 0x06000000, - OT_PI = 0x07000000, - OT_PS = 0x08000000, // actually unsupported for (we don't know its size) - OT_Q = 0x09000000, - OT_S = 0x0A000000, - OT_SS = 0x0B000000, - OT_SI = 0x0C000000, - OT_V = 0x0D000000, - OT_W = 0x0E000000, - OT_SD = 0x0F000000, // scalar double-precision floating-point value - OT_PD = 0x10000000, // double-precision floating point - // dummy "operand type" for address mode M - which doesn't specify - // operand type - OT_ADDRESS_MODE_M = 0x80000000 -}; - -// Everything that's in an Opcode (see below) except the three -// alternative opcode structs for different prefixes. -struct SpecificOpcode { - // Index to continuation table, or 0 if this is the last - // byte in the opcode. - int table_index_; - - // The opcode type - InstructionType type_; - - // Description of the type of the dest, src and aux operands, - // put together from an enOperandType flag and an enAddressingMethod - // flag. - int flag_dest_; - int flag_source_; - int flag_aux_; - - // We indicate the mnemonic for debugging purposes - const char* mnemonic_; -}; - -// The information we keep in our tables about each of the different -// valid instructions recognized by the IA-32 architecture. -struct Opcode { - // Index to continuation table, or 0 if this is the last - // byte in the opcode. - int table_index_; - - // The opcode type - InstructionType type_; - - // Description of the type of the dest, src and aux operands, - // put together from an enOperandType flag and an enAddressingMethod - // flag. - int flag_dest_; - int flag_source_; - int flag_aux_; - - // We indicate the mnemonic for debugging purposes - const char* mnemonic_; - - // Alternative opcode info if certain prefixes are specified. - // In most cases, all of these are zeroed-out. Only used if - // bPrefixDependent is true. - bool is_prefix_dependent_; - SpecificOpcode opcode_if_f2_prefix_; - SpecificOpcode opcode_if_f3_prefix_; - SpecificOpcode opcode_if_66_prefix_; -}; - -// Information about each table entry. -struct OpcodeTable { - // Table of instruction entries - const Opcode* table_; - // How many bytes left to shift ModR/M byte before applying mask - unsigned char shift_; - // Mask to apply to byte being looked at before comparing to table - unsigned char mask_; - // Minimum/maximum indexes in table. - unsigned char min_lim_; - unsigned char max_lim_; -}; - -// Information about each entry in table used to decode ModR/M byte. -struct ModrmEntry { - // Is the operand encoded as bytes in the instruction (rather than - // if it's e.g. a register in which case it's just encoded in the - // ModR/M byte) - bool is_encoded_in_instruction_; - - // Is there a SIB byte? In this case we always need to decode it. - bool use_sib_byte_; - - // What is the size of the operand (only important if it's encoded - // in the instruction)? - OperandSize operand_size_; -}; - -}; // namespace sidestep - -#endif // SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H__ diff --git a/sandbox/src/sidestep/preamble_patcher.h b/sandbox/src/sidestep/preamble_patcher.h deleted file mode 100644 index 18cf7f8..0000000 --- a/sandbox/src/sidestep/preamble_patcher.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Definition of PreamblePatcher - -#ifndef SANDBOX_SRC_SIDESTEP_PREAMBLE_PATCHER_H__ -#define SANDBOX_SRC_SIDESTEP_PREAMBLE_PATCHER_H__ - -namespace sidestep { - -// Maximum size of the preamble stub. We overwrite at least the first 5 -// bytes of the function. Considering the worst case scenario, we need 4 -// bytes + the max instruction size + 5 more bytes for our jump back to -// the original code. With that in mind, 32 is a good number :) -const size_t kMaxPreambleStubSize = 32; - -// Possible results of patching/unpatching -enum SideStepError { - SIDESTEP_SUCCESS = 0, - SIDESTEP_INVALID_PARAMETER, - SIDESTEP_INSUFFICIENT_BUFFER, - SIDESTEP_JUMP_INSTRUCTION, - SIDESTEP_FUNCTION_TOO_SMALL, - SIDESTEP_UNSUPPORTED_INSTRUCTION, - SIDESTEP_NO_SUCH_MODULE, - SIDESTEP_NO_SUCH_FUNCTION, - SIDESTEP_ACCESS_DENIED, - SIDESTEP_UNEXPECTED, -}; - -// Implements a patching mechanism that overwrites the first few bytes of -// a function preamble with a jump to our hook function, which is then -// able to call the original function via a specially-made preamble-stub -// that imitates the action of the original preamble. -// -// Note that there are a number of ways that this method of patching can -// fail. The most common are: -// - If there is a jump (jxx) instruction in the first 5 bytes of -// the function being patched, we cannot patch it because in the -// current implementation we do not know how to rewrite relative -// jumps after relocating them to the preamble-stub. Note that -// if you really really need to patch a function like this, it -// would be possible to add this functionality (but at some cost). -// - If there is a return (ret) instruction in the first 5 bytes -// we cannot patch the function because it may not be long enough -// for the jmp instruction we use to inject our patch. -// - If there is another thread currently executing within the bytes -// that are copied to the preamble stub, it will crash in an undefined -// way. -// -// If you get any other error than the above, you're either pointing the -// patcher at an invalid instruction (e.g. into the middle of a multi- -// byte instruction, or not at memory containing executable instructions) -// or, there may be a bug in the disassembler we use to find -// instruction boundaries. -class PreamblePatcher { - public: - // Patches target_function to point to replacement_function using a provided - // preamble_stub of stub_size bytes. - // Returns An error code indicating the result of patching. - template - static SideStepError Patch(T target_function, T replacement_function, - void* preamble_stub, size_t stub_size) { - return RawPatchWithStub(target_function, replacement_function, - reinterpret_cast(preamble_stub), - stub_size, NULL); - } - - private: - - // Patches a function by overwriting its first few bytes with - // a jump to a different function. This is similar to the RawPatch - // function except that it uses the stub allocated by the caller - // instead of allocating it. - // - // To use this function, you first have to call VirtualProtect to make the - // target function writable at least for the duration of the call. - // - // target_function: A pointer to the function that should be - // patched. - // - // replacement_function: A pointer to the function that should - // replace the target function. The replacement function must have - // exactly the same calling convention and parameters as the original - // function. - // - // preamble_stub: A pointer to a buffer where the preamble stub - // should be copied. The size of the buffer should be sufficient to - // hold the preamble bytes. - // - // stub_size: Size in bytes of the buffer allocated for the - // preamble_stub - // - // bytes_needed: Pointer to a variable that receives the minimum - // number of bytes required for the stub. Can be set to NULL if you're - // not interested. - // - // Returns An error code indicating the result of patching. - static SideStepError RawPatchWithStub(void* target_function, - void *replacement_function, - unsigned char* preamble_stub, - size_t stub_size, - size_t* bytes_needed); -}; - -}; // namespace sidestep - -#endif // SANDBOX_SRC_SIDESTEP_PREAMBLE_PATCHER_H__ diff --git a/sandbox/src/sidestep/preamble_patcher_with_stub.cpp b/sandbox/src/sidestep/preamble_patcher_with_stub.cpp deleted file mode 100644 index 4bdd79c2..0000000 --- a/sandbox/src/sidestep/preamble_patcher_with_stub.cpp +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Implementation of PreamblePatcher - -#include "sandbox/src/sidestep/preamble_patcher.h" - -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sidestep/mini_disassembler.h" - -// Definitions of assembly statements we need -#define ASM_JMP32REL 0xE9 -#define ASM_INT3 0xCC - -namespace { - -// Very basic memcpy. We are copying 4 to 12 bytes most of the time, so there -// is no attempt to optimize this code or have a general purpose function. -// We don't want to call the crt from this code. -inline void* RawMemcpy(void* destination, const void* source, size_t bytes) { - const char* from = reinterpret_cast(source); - char* to = reinterpret_cast(destination); - - for (size_t i = 0; i < bytes ; i++) - to[i] = from[i]; - - return destination; -} - -// Very basic memset. We are filling 1 to 7 bytes most of the time, so there -// is no attempt to optimize this code or have a general purpose function. -// We don't want to call the crt from this code. -inline void* RawMemset(void* destination, int value, size_t bytes) { - char* to = reinterpret_cast(destination); - - for (size_t i = 0; i < bytes ; i++) - to[i] = static_cast(value); - - return destination; -} - -} // namespace - -#define ASSERT(a, b) DCHECK_NT(a) - -namespace sidestep { - -SideStepError PreamblePatcher::RawPatchWithStub( - void* target_function, - void* replacement_function, - unsigned char* preamble_stub, - size_t stub_size, - size_t* bytes_needed) { - if ((NULL == target_function) || - (NULL == replacement_function) || - (NULL == preamble_stub)) { - ASSERT(false, (L"Invalid parameters - either pTargetFunction or " - L"pReplacementFunction or pPreambleStub were NULL.")); - return SIDESTEP_INVALID_PARAMETER; - } - - // TODO(V7:joi) Siggi and I just had a discussion and decided that both - // patching and unpatching are actually unsafe. We also discussed a - // method of making it safe, which is to freeze all other threads in the - // process, check their thread context to see if their eip is currently - // inside the block of instructions we need to copy to the stub, and if so - // wait a bit and try again, then unfreeze all threads once we've patched. - // Not implementing this for now since we're only using SideStep for unit - // testing, but if we ever use it for production code this is what we - // should do. - // - // NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using - // FPU instructions, and on newer processors we could use cmpxchg8b or - // cmpxchg16b. So it might be possible to do the patching/unpatching - // atomically and avoid having to freeze other threads. Note though, that - // doing it atomically does not help if one of the other threads happens - // to have its eip in the middle of the bytes you change while you change - // them. - unsigned char* target = reinterpret_cast(target_function); - - // Let's disassemble the preamble of the target function to see if we can - // patch, and to see how much of the preamble we need to take. We need 5 - // bytes for our jmp instruction, so let's find the minimum number of - // instructions to get 5 bytes. - MiniDisassembler disassembler; - unsigned int preamble_bytes = 0; - while (preamble_bytes < 5) { - InstructionType instruction_type = - disassembler.Disassemble(target + preamble_bytes, &preamble_bytes); - if (IT_JUMP == instruction_type) { - ASSERT(false, (L"Unable to patch because there is a jump instruction " - L"in the first 5 bytes.")); - return SIDESTEP_JUMP_INSTRUCTION; - } else if (IT_RETURN == instruction_type) { - ASSERT(false, (L"Unable to patch because function is too short")); - return SIDESTEP_FUNCTION_TOO_SMALL; - } else if (IT_GENERIC != instruction_type) { - ASSERT(false, (L"Disassembler encountered unsupported instruction " - L"(either unused or unknown")); - return SIDESTEP_UNSUPPORTED_INSTRUCTION; - } - } - - if (NULL != bytes_needed) - *bytes_needed = preamble_bytes + 5; - - // Inv: preamble_bytes is the number of bytes (at least 5) that we need to - // take from the preamble to have whole instructions that are 5 bytes or more - // in size total. The size of the stub required is cbPreamble + size of - // jmp (5) - if (preamble_bytes + 5 > stub_size) { - NOTREACHED_NT(); - return SIDESTEP_INSUFFICIENT_BUFFER; - } - - // First, copy the preamble that we will overwrite. - RawMemcpy(reinterpret_cast(preamble_stub), - reinterpret_cast(target), preamble_bytes); - - // Now, make a jmp instruction to the rest of the target function (minus the - // preamble bytes we moved into the stub) and copy it into our preamble-stub. - // find address to jump to, relative to next address after jmp instruction -#pragma warning(push) -#pragma warning(disable:4244) - // This assignment generates a warning because it is 32 bit specific. - int relative_offset_to_target_rest - = ((reinterpret_cast(target) + preamble_bytes) - - (preamble_stub + preamble_bytes + 5)); -#pragma warning(pop) - // jmp (Jump near, relative, displacement relative to next instruction) - preamble_stub[preamble_bytes] = ASM_JMP32REL; - // copy the address - RawMemcpy(reinterpret_cast(preamble_stub + preamble_bytes + 1), - reinterpret_cast(&relative_offset_to_target_rest), 4); - - // Inv: preamble_stub points to assembly code that will execute the - // original function by first executing the first cbPreamble bytes of the - // preamble, then jumping to the rest of the function. - - // Overwrite the first 5 bytes of the target function with a jump to our - // replacement function. - // (Jump near, relative, displacement relative to next instruction) - target[0] = ASM_JMP32REL; - - // Find offset from instruction after jmp, to the replacement function. -#pragma warning(push) -#pragma warning(disable:4244) - int offset_to_replacement_function = - reinterpret_cast(replacement_function) - - reinterpret_cast(target) - 5; -#pragma warning(pop) - // complete the jmp instruction - RawMemcpy(reinterpret_cast(target + 1), - reinterpret_cast(&offset_to_replacement_function), 4); - // Set any remaining bytes that were moved to the preamble-stub to INT3 so - // as not to cause confusion (otherwise you might see some strange - // instructions if you look at the disassembly, or even invalid - // instructions). Also, by doing this, we will break into the debugger if - // some code calls into this portion of the code. If this happens, it - // means that this function cannot be patched using this patcher without - // further thought. - if (preamble_bytes > 5) { - RawMemset(reinterpret_cast(target + 5), ASM_INT3, - preamble_bytes - 5); - } - - // Inv: The memory pointed to by target_function now points to a relative - // jump instruction that jumps over to the preamble_stub. The preamble - // stub contains the first stub_size bytes of the original target - // function's preamble code, followed by a relative jump back to the next - // instruction after the first cbPreamble bytes. - - return SIDESTEP_SUCCESS; -} - -}; // namespace sidestep - -#undef ASSERT diff --git a/sandbox/src/sidestep_resolver.cc b/sandbox/src/sidestep_resolver.cc deleted file mode 100644 index e22ca6b..0000000 --- a/sandbox/src/sidestep_resolver.cc +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/sidestep_resolver.h" - -#include "base/win/pe_image.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sidestep/preamble_patcher.h" - -namespace { - -const size_t kSizeOfSidestepStub = sidestep::kMaxPreambleStubSize; - -struct SidestepThunk { - char sidestep[kSizeOfSidestepStub]; // Storage for the sidestep stub. - int internal_thunk; // Dummy member to the beginning of the internal thunk. -}; - -struct SmartThunk { - const void* module_base; // Target module's base. - const void* interceptor; // Real interceptor. - SidestepThunk sidestep; // Standard sidestep thunk. -}; - -} // namespace - -namespace sandbox { - -NTSTATUS SidestepResolverThunk::Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) { - NTSTATUS ret = Init(target_module, interceptor_module, target_name, - interceptor_name, interceptor_entry_point, - thunk_storage, storage_bytes); - if (!NT_SUCCESS(ret)) - return ret; - - SidestepThunk* thunk = reinterpret_cast(thunk_storage); - - size_t internal_bytes = storage_bytes - kSizeOfSidestepStub; - if (!SetInternalThunk(&thunk->internal_thunk, internal_bytes, thunk_storage, - interceptor_)) - return STATUS_BUFFER_TOO_SMALL; - - AutoProtectMemory memory; - memory.ChangeProtection(target_, kSizeOfSidestepStub, PAGE_READWRITE); - - sidestep::SideStepError rv = sidestep::PreamblePatcher::Patch( - target_, reinterpret_cast(&thunk->internal_thunk), thunk_storage, - kSizeOfSidestepStub); - - if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER == rv) - return STATUS_BUFFER_TOO_SMALL; - - if (sidestep::SIDESTEP_SUCCESS != rv) - return STATUS_UNSUCCESSFUL; - - if (storage_used) - *storage_used = GetThunkSize(); - - return ret; -} - -size_t SidestepResolverThunk::GetThunkSize() const { - return GetInternalThunkSize() + kSizeOfSidestepStub; -} - -// This is basically a wrapper around the normal sidestep patch that extends -// the thunk to use a chained interceptor. It uses the fact that -// SetInternalThunk generates the code to pass as the first parameter whatever -// it receives as original_function; we let SidestepResolverThunk set this value -// to its saved code, and then we change it to our thunk data. -NTSTATUS SmartSidestepResolverThunk::Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) { - if (storage_bytes < GetThunkSize()) - return STATUS_BUFFER_TOO_SMALL; - - SmartThunk* thunk = reinterpret_cast(thunk_storage); - thunk->module_base = target_module; - - NTSTATUS ret; - if (interceptor_entry_point) { - thunk->interceptor = interceptor_entry_point; - } else { - ret = ResolveInterceptor(interceptor_module, interceptor_name, - &thunk->interceptor); - if (!NT_SUCCESS(ret)) - return ret; - } - - // Perform a standard sidestep patch on the last part of the thunk, but point - // to our internal smart interceptor. - size_t standard_bytes = storage_bytes - offsetof(SmartThunk, sidestep); - ret = SidestepResolverThunk::Setup(target_module, interceptor_module, - target_name, NULL, &SmartStub, - &thunk->sidestep, standard_bytes, NULL); - if (!NT_SUCCESS(ret)) - return ret; - - // Fix the internal thunk to pass the whole buffer to the interceptor. - SetInternalThunk(&thunk->sidestep.internal_thunk, GetInternalThunkSize(), - thunk_storage, &SmartStub); - - if (storage_used) - *storage_used = GetThunkSize(); - - return ret; -} - -size_t SmartSidestepResolverThunk::GetThunkSize() const { - return GetInternalThunkSize() + kSizeOfSidestepStub + - offsetof(SmartThunk, sidestep); -} - -// This code must basically either call the intended interceptor or skip the -// call and invoke instead the original function. In any case, we are saving -// the registers that may be trashed by our c++ code. -// -// This function is called with a first parameter inserted by us, that points -// to our SmartThunk. When we call the interceptor we have to replace this -// parameter with the one expected by that function (stored inside our -// structure); on the other hand, when we skip the interceptor we have to remove -// that extra argument before calling the original function. -// -// When we skip the interceptor, the transformation of the stack looks like: -// On Entry: On Use: On Exit: -// [param 2] = first real argument [param 2] (esp+1c) [param 2] -// [param 1] = our SmartThunk [param 1] (esp+18) [ret address] -// [ret address] = real caller [ret address] (esp+14) [xxx] -// [xxx] [addr to jump to] (esp+10) [xxx] -// [xxx] [saved eax] [xxx] -// [xxx] [saved ebx] [xxx] -// [xxx] [saved ecx] [xxx] -// [xxx] [saved edx] [xxx] -__declspec(naked) -void SmartSidestepResolverThunk::SmartStub() { - __asm { - push eax // Space for the jump. - push eax // Save registers. - push ebx - push ecx - push edx - mov ebx, [esp + 0x18] // First parameter = SmartThunk. - mov edx, [esp + 0x14] // Get the return address. - mov eax, [ebx]SmartThunk.module_base - push edx - push eax - call SmartSidestepResolverThunk::IsInternalCall - add esp, 8 - - test eax, eax - lea edx, [ebx]SmartThunk.sidestep // The original function. - jz call_interceptor - - // Skip this call - mov ecx, [esp + 0x14] // Return address. - mov [esp + 0x18], ecx // Remove first parameter. - mov [esp + 0x10], edx - pop edx // Restore registers. - pop ecx - pop ebx - pop eax - ret 4 // Jump to original function. - - call_interceptor: - mov ecx, [ebx]SmartThunk.interceptor - mov [esp + 0x18], edx // Replace first parameter. - mov [esp + 0x10], ecx - pop edx // Restore registers. - pop ecx - pop ebx - pop eax - ret // Jump to original function. - } -} - -bool SmartSidestepResolverThunk::IsInternalCall(const void* base, - void* return_address) { - DCHECK_NT(base); - DCHECK_NT(return_address); - - base::win::PEImage pe(base); - if (pe.GetImageSectionFromAddr(return_address)) - return true; - return false; -} - -} // namespace sandbox diff --git a/sandbox/src/sidestep_resolver.h b/sandbox/src/sidestep_resolver.h deleted file mode 100644 index 1e7fdac..0000000 --- a/sandbox/src/sidestep_resolver.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SIDESTEP_RESOLVER_H__ -#define SANDBOX_SRC_SIDESTEP_RESOLVER_H__ - -#include "base/basictypes.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/resolver.h" - -namespace sandbox { - -// This is the concrete resolver used to perform sidestep interceptions. -class SidestepResolverThunk : public ResolverThunk { - public: - SidestepResolverThunk() {} - virtual ~SidestepResolverThunk() {} - - // Implementation of Resolver::Setup. - virtual NTSTATUS Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used); - - // Implementation of Resolver::GetThunkSize. - virtual size_t GetThunkSize() const; - - private: - DISALLOW_COPY_AND_ASSIGN(SidestepResolverThunk); -}; - -// This is the concrete resolver used to perform smart sidestep interceptions. -// This means basically a sidestep interception that skips the interceptor when -// the caller resides on the same dll being intercepted. It is intended as -// a helper only, because that determination is not infallible. -class SmartSidestepResolverThunk : public SidestepResolverThunk { - public: - SmartSidestepResolverThunk() {} - virtual ~SmartSidestepResolverThunk() {} - - // Implementation of Resolver::Setup. - virtual NTSTATUS Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used); - - // Implementation of Resolver::GetThunkSize. - virtual size_t GetThunkSize() const; - - private: - // Performs the actual call to the interceptor if the conditions are correct - // (as determined by IsInternalCall). - static void SmartStub(); - - // Returns true if return_address is inside the module loaded at base. - static bool IsInternalCall(const void* base, void* return_address); - - DISALLOW_COPY_AND_ASSIGN(SmartSidestepResolverThunk); -}; - -} // namespace sandbox - - -#endif // SANDBOX_SRC_SIDESTEP_RESOLVER_H__ diff --git a/sandbox/src/sync_dispatcher.cc b/sandbox/src/sync_dispatcher.cc deleted file mode 100644 index 025fd96..0000000 --- a/sandbox/src/sync_dispatcher.cc +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/sync_dispatcher.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/interception.h" -#include "sandbox/src/interceptors.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_broker.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sync_interception.h" -#include "sandbox/src/sync_policy.h" - -namespace sandbox { - -SyncDispatcher::SyncDispatcher(PolicyBase* policy_base) - : policy_base_(policy_base) { - static const IPCCall create_params = { - {IPC_CREATEEVENT_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE}, - reinterpret_cast(&SyncDispatcher::CreateEvent) - }; - - static const IPCCall open_params = { - {IPC_OPENEVENT_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE}, - reinterpret_cast(&SyncDispatcher::OpenEvent) - }; - - ipc_calls_.push_back(create_params); - ipc_calls_.push_back(open_params); -} - -bool SyncDispatcher::SetupService(InterceptionManager* manager, - int service) { - if (IPC_CREATEEVENT_TAG == service) - return INTERCEPT_EAT(manager, L"kernel32.dll", CreateEventW, - CREATE_EVENT_ID, 20); - - if (IPC_OPENEVENT_TAG == service) - return INTERCEPT_EAT(manager, L"kernel32.dll", OpenEventW, - OPEN_EVENT_ID, 16); - - return false; -} - -bool SyncDispatcher::CreateEvent(IPCInfo* ipc, std::wstring* name, - DWORD manual_reset, DWORD initial_state) { - const wchar_t* event_name = name->c_str(); - CountedParameterSet params; - params[NameBased::NAME] = ParamPickerMake(event_name); - - EvalResult result = policy_base_->EvalPolicy(IPC_CREATEEVENT_TAG, - params.GetBase()); - HANDLE handle = NULL; - DWORD ret = SyncPolicy::CreateEventAction(result, *ipc->client_info, *name, - manual_reset, initial_state, - &handle); - // Return operation status on the IPC. - ipc->return_info.win32_result = ret; - ipc->return_info.handle = handle; - return true; -} - -bool SyncDispatcher::OpenEvent(IPCInfo* ipc, std::wstring* name, - DWORD desired_access, DWORD inherit_handle) { - const wchar_t* event_name = name->c_str(); - - CountedParameterSet params; - params[OpenEventParams::NAME] = ParamPickerMake(event_name); - params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access); - - EvalResult result = policy_base_->EvalPolicy(IPC_OPENEVENT_TAG, - params.GetBase()); - HANDLE handle = NULL; - DWORD ret = SyncPolicy::OpenEventAction(result, *ipc->client_info, *name, - desired_access, inherit_handle, - &handle); - // Return operation status on the IPC. - ipc->return_info.win32_result = ret; - ipc->return_info.handle = handle; - return true; -} - -} // namespace sandbox diff --git a/sandbox/src/sync_dispatcher.h b/sandbox/src/sync_dispatcher.h deleted file mode 100644 index be9b9a2..0000000 --- a/sandbox/src/sync_dispatcher.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SYNC_DISPATCHER_H_ -#define SANDBOX_SRC_SYNC_DISPATCHER_H_ - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sandbox_policy_base.h" - -namespace sandbox { - -// This class handles sync-related IPC calls. -class SyncDispatcher : public Dispatcher { - public: - explicit SyncDispatcher(PolicyBase* policy_base); - ~SyncDispatcher() {} - - // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); - -private: - // Processes IPC requests coming from calls to CreateEvent in the target. - bool CreateEvent(IPCInfo* ipc, std::wstring* name, DWORD manual_reset, - DWORD initial_state); - - // Processes IPC requests coming from calls to OpenEvent in the target. - bool OpenEvent(IPCInfo* ipc, std::wstring* name, DWORD desired_access, - DWORD inherit_handle); - - PolicyBase* policy_base_; - DISALLOW_COPY_AND_ASSIGN(SyncDispatcher); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SYNC_DISPATCHER_H_ diff --git a/sandbox/src/sync_interception.cc b/sandbox/src/sync_interception.cc deleted file mode 100644 index 4832c79..0000000 --- a/sandbox/src/sync_interception.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/sync_interception.h" - -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/policy_target.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/target_services.h" - -namespace sandbox { - -HANDLE WINAPI TargetCreateEventW(CreateEventWFunction orig_CreateEvent, - LPSECURITY_ATTRIBUTES security_attributes, - BOOL manual_reset, BOOL initial_state, - LPCWSTR name) { - // Check if the process can create it first. - HANDLE handle = orig_CreateEvent(security_attributes, manual_reset, - initial_state, name); - DWORD original_error = ::GetLastError(); - if (NULL != handle) - return handle; - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return NULL; - - do { - if (security_attributes) - break; - - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - CountedParameterSet params; - params[NameBased::NAME] = ParamPickerMake(name); - - if (!QueryBroker(IPC_CREATEEVENT_TAG, params.GetBase())) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_CREATEEVENT_TAG, name, manual_reset, - initial_state, &answer); - - if (SBOX_ALL_OK != code) - break; - - ::SetLastError(answer.win32_result); - return answer.handle; - } while (false); - - ::SetLastError(original_error); - return NULL; -} - -// Interception of OpenEventW on the child process. -// It should never be called directly -HANDLE WINAPI TargetOpenEventW(OpenEventWFunction orig_OpenEvent, - ACCESS_MASK desired_access, BOOL inherit_handle, - LPCWSTR name) { - // Check if the process can open it first. - HANDLE handle = orig_OpenEvent(desired_access, inherit_handle, name); - DWORD original_error = ::GetLastError(); - if (NULL != handle) - return handle; - - // We don't trust that the IPC can work this early. - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return NULL; - - do { - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) - break; - - uint32 inherit_handle_ipc = inherit_handle; - CountedParameterSet params; - params[OpenEventParams::NAME] = ParamPickerMake(name); - params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access); - - if (!QueryBroker(IPC_OPENEVENT_TAG, params.GetBase())) - break; - - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - ResultCode code = CrossCall(ipc, IPC_OPENEVENT_TAG, name, desired_access, - inherit_handle_ipc, &answer); - - if (SBOX_ALL_OK != code) - break; - - ::SetLastError(answer.win32_result); - return answer.handle; - } while (false); - - ::SetLastError(original_error); - return NULL; -} - -} // namespace sandbox diff --git a/sandbox/src/sync_interception.h b/sandbox/src/sync_interception.h deleted file mode 100644 index 4ac23e7..0000000 --- a/sandbox/src/sync_interception.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -#ifndef SANDBOX_SRC_SYNC_INTERCEPTION_H__ -#define SANDBOX_SRC_SYNC_INTERCEPTION_H__ - -namespace sandbox { - -extern "C" { - -typedef HANDLE (WINAPI *CreateEventWFunction) ( - LPSECURITY_ATTRIBUTES lpEventAttributes, - DWORD dwDesiredAccess, - BOOL bInheritHandle, - LPCWSTR lpName); - -typedef HANDLE (WINAPI *OpenEventWFunction) ( - BOOL bManualReset, - BOOL bInitialState, - LPCWSTR lpName); - -// Interception of CreateEvent on the child process. -SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW( - CreateEventWFunction orig_CreateEvent, - LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset, - BOOL initial_state, LPCWSTR name); - -// Interception of OpenEvent on the child process. -SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW( - OpenEventWFunction orig_OpenEvent, ACCESS_MASK desired_access, - BOOL inherit_handle, LPCWSTR name); - -} // extern "C" - -} // namespace sandbox - -#endif // SANDBOX_SRC_SYNC_INTERCEPTION_H__ diff --git a/sandbox/src/sync_policy.cc b/sandbox/src/sync_policy.cc deleted file mode 100644 index 364cf73..0000000 --- a/sandbox/src/sync_policy.cc +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "sandbox/src/sync_policy.h" - -#include "base/logging.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/policy_engine_opcodes.h" -#include "sandbox/src/policy_params.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/sandbox_utils.h" - -namespace sandbox { - -bool SyncPolicy::GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy) { - std::wstring mod_name(name); - if (mod_name.empty()) { - return false; - } - - if (TargetPolicy::EVENTS_ALLOW_ANY != semantics && - TargetPolicy::EVENTS_ALLOW_READONLY != semantics) { - // Other flags are not valid for sync policy yet. - NOTREACHED(); - return false; - } - - // Add the open rule. - EvalResult result = ASK_BROKER; - PolicyRule open(result); - - if (!open.AddStringMatch(IF, OpenEventParams::NAME, name, CASE_INSENSITIVE)) - return false; - - if (TargetPolicy::EVENTS_ALLOW_READONLY == semantics) { - // We consider all flags that are not known to be readonly as potentially - // used for write. - DWORD allowed_flags = SYNCHRONIZE | GENERIC_READ | READ_CONTROL; - DWORD restricted_flags = ~allowed_flags; - open.AddNumberMatch(IF_NOT, OpenEventParams::ACCESS, restricted_flags, AND); - } - - if (!policy->AddRule(IPC_OPENEVENT_TAG, &open)) - return false; - - // If it's not a read only, add the create rule. - if (TargetPolicy::EVENTS_ALLOW_READONLY != semantics) { - PolicyRule create(result); - if (!create.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) - return false; - - if (!policy->AddRule(IPC_CREATEEVENT_TAG, &create)) - return false; - } - - return true; -} - -DWORD SyncPolicy::CreateEventAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &event_name, - uint32 manual_reset, - uint32 initial_state, - HANDLE *handle) { - // The only action supported is ASK_BROKER which means create the requested - // file as specified. - if (ASK_BROKER != eval_result) - return false; - - HANDLE local_handle = ::CreateEvent(NULL, manual_reset, initial_state, - event_name.c_str()); - if (NULL == local_handle) - return ::GetLastError(); - - if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - client_info.process, handle, 0, FALSE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(local_handle); - return ERROR_ACCESS_DENIED; - } - return ERROR_SUCCESS; -} - -DWORD SyncPolicy::OpenEventAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &event_name, - uint32 desired_access, - uint32 inherit_handle, - HANDLE *handle) { - // The only action supported is ASK_BROKER which means create the requested - // file as specified. - if (ASK_BROKER != eval_result) - return false; - - HANDLE local_handle = ::OpenEvent(desired_access, FALSE, - event_name.c_str()); - if (NULL == local_handle) - return ::GetLastError(); - - if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, - client_info.process, handle, 0, inherit_handle, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { - ::CloseHandle(local_handle); - return ERROR_ACCESS_DENIED; - } - return ERROR_SUCCESS; -} - -} // namespace sandbox diff --git a/sandbox/src/sync_policy.h b/sandbox/src/sync_policy.h deleted file mode 100644 index 6404f79..0000000 --- a/sandbox/src/sync_policy.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_SYNC_POLICY_H__ -#define SANDBOX_SRC_SYNC_POLICY_H__ - -#include - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/policy_low_level.h" -#include "sandbox/src/sandbox_policy.h" - -namespace sandbox { - -enum EvalResult; - -// This class centralizes most of the knowledge related to sync policy -class SyncPolicy { - public: - // Creates the required low-level policy rules to evaluate a high-level - // policy rule for sync calls, in particular open or create actions. - // name is the sync object name, semantics is the desired semantics for the - // open or create and policy is the policy generator to which the rules are - // going to be added. - static bool GenerateRules(const wchar_t* name, - TargetPolicy::Semantics semantics, - LowLevelPolicy* policy); - - // Performs the desired policy action on a request. - // client_info is the target process that is making the request and - // eval_result is the desired policy action to accomplish. - static DWORD CreateEventAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &event_name, - uint32 manual_reset, - uint32 initial_state, - HANDLE *handle); - static DWORD OpenEventAction(EvalResult eval_result, - const ClientInfo& client_info, - const std::wstring &event_name, - uint32 desired_access, - uint32 inherit_handle, - HANDLE *handle); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_SYNC_POLICY_H__ diff --git a/sandbox/src/sync_policy_test.cc b/sandbox/src/sync_policy_test.cc deleted file mode 100644 index 3c87243..0000000 --- a/sandbox/src/sync_policy_test.cc +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/win/scoped_handle.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_policy.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/nt_internals.h" -#include "sandbox/tests/common/controller.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t **argv) { - if (argc != 2) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - DWORD desired_access = SYNCHRONIZE; - if (L'f' == argv[0][0]) - desired_access = EVENT_ALL_ACCESS; - - base::win::ScopedHandle event_open(::OpenEvent( - desired_access, FALSE, argv[1])); - DWORD error_open = ::GetLastError(); - - if (event_open.Get()) - return SBOX_TEST_SUCCEEDED; - - if (ERROR_ACCESS_DENIED == error_open || - ERROR_BAD_PATHNAME == error_open) - return SBOX_TEST_DENIED; - - return SBOX_TEST_FAILED; -} - -SBOX_TESTS_COMMAND int Event_CreateOpen(int argc, wchar_t **argv) { - if (argc < 2 || argc > 3) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - wchar_t *event_name = NULL; - if (3 == argc) - event_name = argv[2]; - - BOOL manual_reset = FALSE; - BOOL initial_state = FALSE; - if (L't' == argv[0][0]) - manual_reset = TRUE; - if (L't' == argv[1][0]) - initial_state = TRUE; - - base::win::ScopedHandle event_create(::CreateEvent( - NULL, manual_reset, initial_state, event_name)); - DWORD error_create = ::GetLastError(); - base::win::ScopedHandle event_open; - if (event_name) - event_open.Set(::OpenEvent(EVENT_ALL_ACCESS, FALSE, event_name)); - - if (event_create.Get()) { - DWORD wait = ::WaitForSingleObject(event_create.Get(), 0); - if (initial_state && WAIT_OBJECT_0 != wait) - return SBOX_TEST_FAILED; - - if (!initial_state && WAIT_TIMEOUT != wait) - return SBOX_TEST_FAILED; - } - - if (event_name) { - // Both event_open and event_create have to be valid. - if (event_open.Get() && event_create) - return SBOX_TEST_SUCCEEDED; - - if (event_open.Get() && !event_create || !event_open.Get() && event_create) - return SBOX_TEST_FAILED; - } else { - // Only event_create has to be valid. - if (event_create.Get()) - return SBOX_TEST_SUCCEEDED; - } - - if (ERROR_ACCESS_DENIED == error_create || - ERROR_BAD_PATHNAME == error_create) - return SBOX_TEST_DENIED; - - return SBOX_TEST_FAILED; -} - -// Tests the creation of events using all the possible combinations. -TEST(SyncPolicyTest, TestEvent) { - TestRunner runner; - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, - TargetPolicy::EVENTS_ALLOW_ANY, - L"test1")); - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, - TargetPolicy::EVENTS_ALLOW_ANY, - L"test2")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f test1")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f test2")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t test1")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t test2")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test3")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test4")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test3")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test4")); -} - -// Tests opening events with read only access. -TEST(SyncPolicyTest, TestEventReadOnly) { - TestRunner runner; - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, - TargetPolicy::EVENTS_ALLOW_READONLY, - L"test1")); - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, - TargetPolicy::EVENTS_ALLOW_READONLY, - L"test2")); - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, - TargetPolicy::EVENTS_ALLOW_READONLY, - L"test5")); - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, - TargetPolicy::EVENTS_ALLOW_READONLY, - L"test6")); - - base::win::ScopedHandle handle1(::CreateEvent(NULL, FALSE, FALSE, L"test1")); - base::win::ScopedHandle handle2(::CreateEvent(NULL, FALSE, FALSE, L"test2")); - base::win::ScopedHandle handle3(::CreateEvent(NULL, FALSE, FALSE, L"test3")); - base::win::ScopedHandle handle4(::CreateEvent(NULL, FALSE, FALSE, L"test4")); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test1")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_Open s test2")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test3")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open s test4")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test5")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test6")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test5")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test6")); -} - -} // namespace sandbox diff --git a/sandbox/src/target_interceptions.cc b/sandbox/src/target_interceptions.cc deleted file mode 100644 index a1d463b..0000000 --- a/sandbox/src/target_interceptions.cc +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/target_interceptions.h" - -#include "sandbox/src/interception_agent.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_nt_util.h" -#include "sandbox/src/target_services.h" - -namespace sandbox { - -SANDBOX_INTERCEPT NtExports g_nt; - -// Hooks NtMapViewOfSection to detect the load of DLLs. If hot patching is -// required for this dll, this functions patches it. -NTSTATUS WINAPI TargetNtMapViewOfSection( - NtMapViewOfSectionFunction orig_MapViewOfSection, HANDLE section, - HANDLE process, PVOID *base, ULONG_PTR zero_bits, SIZE_T commit_size, - PLARGE_INTEGER offset, PSIZE_T view_size, SECTION_INHERIT inherit, - ULONG allocation_type, ULONG protect) { - NTSTATUS ret = orig_MapViewOfSection(section, process, base, zero_bits, - commit_size, offset, view_size, inherit, - allocation_type, protect); - - static int s_load_count = 0; - if (1 == s_load_count) { - SandboxFactory::GetTargetServices()->GetState()->SetKernel32Loaded(); - s_load_count = 2; - } - - do { - if (!NT_SUCCESS(ret)) - break; - - if (!InitHeap()) - break; - - if (!IsSameProcess(process)) - break; - - if (!IsValidImageSection(section, base, offset, view_size)) - break; - - UINT image_flags; - UNICODE_STRING* module_name = - GetImageInfoFromModule(reinterpret_cast(*base), &image_flags); - UNICODE_STRING* file_name = GetBackingFilePath(*base); - - if ((!module_name) && (image_flags & MODULE_HAS_CODE)) { - // If the module has no exports we retrieve the module name from the - // full path of the mapped section. - module_name = ExtractModuleName(file_name); - } - - InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent(); - - if (agent) { - if (!agent->OnDllLoad(file_name, module_name, *base)) { - // Interception agent is demanding to un-map the module. - g_nt.UnmapViewOfSection(process, *base); - ret = STATUS_UNSUCCESSFUL; - } - } - - if (module_name) - operator delete(module_name, NT_ALLOC); - - if (file_name) - operator delete(file_name, NT_ALLOC); - - } while (false); - - if (!s_load_count) - s_load_count = 1; - - return ret; -} - -NTSTATUS WINAPI TargetNtUnmapViewOfSection( - NtUnmapViewOfSectionFunction orig_UnmapViewOfSection, HANDLE process, - PVOID base) { - NTSTATUS ret = orig_UnmapViewOfSection(process, base); - - if (!NT_SUCCESS(ret)) - return ret; - - if (!IsSameProcess(process)) - return ret; - - InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent(); - - if (agent) - agent->OnDllUnload(base); - - return ret; -} - -} // namespace sandbox diff --git a/sandbox/src/target_interceptions.h b/sandbox/src/target_interceptions.h deleted file mode 100644 index dd6e12c..0000000 --- a/sandbox/src/target_interceptions.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/sandbox_types.h" - -#ifndef SANDBOX_SRC_TARGET_INTERCEPTIONS_H__ -#define SANDBOX_SRC_TARGET_INTERCEPTIONS_H__ - -namespace sandbox { - -extern "C" { - -// Interception of NtMapViewOfSection on the child process. -// It should never be called directly. This function provides the means to -// detect dlls being loaded, so we can patch them if needed. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtMapViewOfSection( - NtMapViewOfSectionFunction orig_MapViewOfSection, HANDLE section, - HANDLE process, PVOID *base, ULONG_PTR zero_bits, SIZE_T commit_size, - PLARGE_INTEGER offset, PSIZE_T view_size, SECTION_INHERIT inherit, - ULONG allocation_type, ULONG protect); - -// Interception of NtUnmapViewOfSection on the child process. -// It should never be called directly. This function provides the means to -// detect dlls being unloaded, so we can clean up our interceptions. -SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtUnmapViewOfSection( - NtUnmapViewOfSectionFunction orig_UnmapViewOfSection, HANDLE process, - PVOID base); - -} // extern "C" - -} // namespace sandbox - -#endif // SANDBOX_SRC_TARGET_INTERCEPTIONS_H__ diff --git a/sandbox/src/target_process.cc b/sandbox/src/target_process.cc deleted file mode 100644 index 88e2751..0000000 --- a/sandbox/src/target_process.cc +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/target_process.h" - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/win/pe_image.h" -#include "base/win/windows_version.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/policy_low_level.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/sharedmem_ipc_server.h" - -namespace { - -void CopyPolicyToTarget(const void* source, size_t size, void* dest) { - if (!source || !size) - return; - memcpy(dest, source, size); - sandbox::PolicyGlobal* policy = - reinterpret_cast(dest); - - size_t offset = reinterpret_cast(source); - - for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) { - size_t buffer = reinterpret_cast(policy->entry[i]); - if (buffer) { - buffer -= offset; - policy->entry[i] = reinterpret_cast(buffer); - } - } -} - -// Reserve a random range at the bottom of the address space in the target -// process to prevent predictable alocations at low addresses. -void PoisonLowerAddressRange(HANDLE process) { - unsigned int limit; - rand_s(&limit); - char* ptr = 0; - const size_t kMask64k = 0xFFFF; - // Random range (512k-16.5mb) in 64k steps. - const char* end = ptr + ((((limit % 16384) + 512) * 1024) & ~kMask64k); - while (ptr < end) { - MEMORY_BASIC_INFORMATION memory_info; - if (!::VirtualQueryEx(process, ptr, &memory_info, sizeof(memory_info))) - break; - size_t size = std::min((memory_info.RegionSize + kMask64k) & ~kMask64k, - static_cast(end - ptr)); - if (ptr && memory_info.State == MEM_FREE) - ::VirtualAllocEx(process, ptr, size, MEM_RESERVE, PAGE_NOACCESS); - ptr += size; - } -} - -} - -namespace sandbox { - -SANDBOX_INTERCEPT HANDLE g_shared_section; -SANDBOX_INTERCEPT size_t g_shared_IPC_size; -SANDBOX_INTERCEPT size_t g_shared_policy_size; - -// Returns the address of the main exe module in memory taking in account -// address space layout randomization. -void* GetBaseAddress(const wchar_t* exe_name, void* entry_point) { - HMODULE exe = ::LoadLibrary(exe_name); - if (NULL == exe) - return exe; - - base::win::PEImage pe(exe); - if (!pe.VerifyMagic()) { - ::FreeLibrary(exe); - return exe; - } - PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders(); - char* base = reinterpret_cast(entry_point) - - nt_header->OptionalHeader.AddressOfEntryPoint; - - ::FreeLibrary(exe); - return base; -} - - -TargetProcess::TargetProcess(HANDLE initial_token, HANDLE lockdown_token, - HANDLE job, ThreadProvider* thread_pool) - // This object owns everything initialized here except thread_pool and - // the job_ handle. The Job handle is closed by BrokerServices and results - // eventually in a call to our dtor. - : lockdown_token_(lockdown_token), - initial_token_(initial_token), - job_(job), - thread_pool_(thread_pool), - base_address_(NULL) { -} - -TargetProcess::~TargetProcess() { - DWORD exit_code = 0; - // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE - // will take effect only when the context changes. As far as the testing went, - // this wait was enough to switch context and kill the processes in the job. - // If this process is already dead, the function will return without waiting. - // TODO(nsylvain): If the process is still alive at the end, we should kill - // it. http://b/893891 - // For now, this wait is there only to do a best effort to prevent some leaks - // from showing up in purify. - ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50); - if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(), - &exit_code) || (STILL_ACTIVE == exit_code)) { - // It is an error to destroy this object while the target process is still - // alive because we need to destroy the IPC subsystem and cannot risk to - // have an IPC reach us after this point. - shared_section_.Take(); - SharedMemIPCServer* server = ipc_server_.release(); - sandbox_process_info_.TakeProcessHandle(); - return; - } - - // ipc_server_ references our process handle, so make sure the former is shut - // down before the latter is closed (by ScopedProcessInformation). - ipc_server_.reset(); -} - -// Creates the target (child) process suspended and assigns it to the job -// object. -DWORD TargetProcess::Create(const wchar_t* exe_path, - const wchar_t* command_line, - const wchar_t* desktop, - base::win::ScopedProcessInformation* target_info) { - exe_name_.reset(_wcsdup(exe_path)); - - // the command line needs to be writable by CreateProcess(). - scoped_ptr_malloc cmd_line(_wcsdup(command_line)); - scoped_ptr_malloc desktop_name(desktop ? _wcsdup(desktop) : NULL); - - // Start the target process suspended. - DWORD flags = - CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS; - - if (base::win::GetVersion() < base::win::VERSION_WIN8) { - // Windows 8 implements nested jobs, but for older systems we need to - // break out of any job we're in to enforce our restrictions. - flags |= CREATE_BREAKAWAY_FROM_JOB; - } - - STARTUPINFO startup_info = {sizeof(STARTUPINFO)}; - if (desktop) { - startup_info.lpDesktop = desktop_name.get(); - } - - base::win::ScopedProcessInformation process_info; - - if (!::CreateProcessAsUserW(lockdown_token_, - exe_path, - cmd_line.get(), - NULL, // No security attribute. - NULL, // No thread attribute. - FALSE, // Do not inherit handles. - flags, - NULL, // Use the environment of the caller. - NULL, // Use current directory of the caller. - &startup_info, - process_info.Receive())) { - return ::GetLastError(); - } - lockdown_token_.Close(); - - PoisonLowerAddressRange(process_info.process_handle()); - - DWORD win_result = ERROR_SUCCESS; - - // Assign the suspended target to the windows job object - if (!::AssignProcessToJobObject(job_, process_info.process_handle())) { - win_result = ::GetLastError(); - // It might be a security breach if we let the target run outside the job - // so kill it before it causes damage - ::TerminateProcess(process_info.process_handle(), 0); - return win_result; - } - - // Change the token of the main thread of the new process for the - // impersonation token with more rights. This allows the target to start; - // otherwise it will crash too early for us to help. - { - HANDLE temp_thread = process_info.thread_handle(); - if (!::SetThreadToken(&temp_thread, initial_token_)) { - win_result = ::GetLastError(); - ::TerminateProcess(process_info.process_handle(), 0); - return win_result; - } - initial_token_.Close(); - } - - CONTEXT context; - context.ContextFlags = CONTEXT_ALL; - if (!::GetThreadContext(process_info.thread_handle(), &context)) { - win_result = ::GetLastError(); - ::TerminateProcess(process_info.process_handle(), 0); - return win_result; - } - -#if defined(_WIN64) - void* entry_point = reinterpret_cast(context.Rcx); -#else -#pragma warning(push) -#pragma warning(disable: 4312) - // This cast generates a warning because it is 32 bit specific. - void* entry_point = reinterpret_cast(context.Eax); -#pragma warning(pop) -#endif // _WIN64 - - if (!target_info->DuplicateFrom(process_info)) { - win_result = ::GetLastError(); // This may or may not be correct. - ::TerminateProcess(process_info.process_handle(), 0); - return win_result; - } - - base_address_ = GetBaseAddress(exe_path, entry_point); - sandbox_process_info_.Swap(&process_info); - return win_result; -} - -ResultCode TargetProcess::TransferVariable(const char* name, void* address, - size_t size) { - if (!sandbox_process_info_.IsValid()) - return SBOX_ERROR_UNEXPECTED_CALL; - - void* child_var = address; - -#if SANDBOX_EXPORTS - HMODULE module = ::LoadLibrary(exe_name_.get()); - if (NULL == module) - return SBOX_ERROR_GENERIC; - - child_var = ::GetProcAddress(module, name); - ::FreeLibrary(module); - - if (NULL == child_var) - return SBOX_ERROR_GENERIC; - - size_t offset = reinterpret_cast(child_var) - - reinterpret_cast(module); - child_var = reinterpret_cast(MainModule()) + offset; -#else - UNREFERENCED_PARAMETER(name); -#endif - - SIZE_T written; - if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), - child_var, address, size, &written)) - return SBOX_ERROR_GENERIC; - - if (written != size) - return SBOX_ERROR_GENERIC; - - return SBOX_ALL_OK; -} - -// Construct the IPC server and the IPC dispatcher. When the target does -// an IPC it will eventually call the dispatcher. -DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy, - uint32 shared_IPC_size, uint32 shared_policy_size) { - // We need to map the shared memory on the target. This is necessary for - // any IPC that needs to take place, even if the target has not yet hit - // the main( ) function or even has initialized the CRT. So here we set - // the handle to the shared section. The target on the first IPC must do - // the rest, which boils down to calling MapViewofFile() - - // We use this single memory pool for IPC and for policy. - DWORD shared_mem_size = static_cast(shared_IPC_size + - shared_policy_size); - shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, - PAGE_READWRITE | SEC_COMMIT, - 0, shared_mem_size, NULL)); - if (!shared_section_.IsValid()) { - return ::GetLastError(); - } - - DWORD access = FILE_MAP_READ | FILE_MAP_WRITE; - HANDLE target_shared_section; - if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_, - sandbox_process_info_.process_handle(), - &target_shared_section, access, FALSE, 0)) { - return ::GetLastError(); - } - - void* shared_memory = ::MapViewOfFile(shared_section_, - FILE_MAP_WRITE|FILE_MAP_READ, - 0, 0, 0); - if (NULL == shared_memory) { - return ::GetLastError(); - } - - CopyPolicyToTarget(policy, shared_policy_size, - reinterpret_cast(shared_memory) + shared_IPC_size); - - ResultCode ret; - // Set the global variables in the target. These are not used on the broker. - g_shared_section = target_shared_section; - ret = TransferVariable("g_shared_section", &g_shared_section, - sizeof(g_shared_section)); - g_shared_section = NULL; - if (SBOX_ALL_OK != ret) { - return (SBOX_ERROR_GENERIC == ret)? - ::GetLastError() : ERROR_INVALID_FUNCTION; - } - g_shared_IPC_size = shared_IPC_size; - ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size, - sizeof(g_shared_IPC_size)); - g_shared_IPC_size = 0; - if (SBOX_ALL_OK != ret) { - return (SBOX_ERROR_GENERIC == ret) ? - ::GetLastError() : ERROR_INVALID_FUNCTION; - } - g_shared_policy_size = shared_policy_size; - ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size, - sizeof(g_shared_policy_size)); - g_shared_policy_size = 0; - if (SBOX_ALL_OK != ret) { - return (SBOX_ERROR_GENERIC == ret) ? - ::GetLastError() : ERROR_INVALID_FUNCTION; - } - - ipc_server_.reset( - new SharedMemIPCServer(sandbox_process_info_.process_handle(), - sandbox_process_info_.process_id(), - job_, thread_pool_, ipc_dispatcher)); - - if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize)) - return ERROR_NOT_ENOUGH_MEMORY; - - // After this point we cannot use this handle anymore. - ::CloseHandle(sandbox_process_info_.TakeThreadHandle()); - - return ERROR_SUCCESS; -} - -void TargetProcess::Terminate() { - if (!sandbox_process_info_.IsValid()) - return; - - ::TerminateProcess(sandbox_process_info_.process_handle(), 0); -} - - -TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) { - TargetProcess* target = new TargetProcess(NULL, NULL, NULL, NULL); - target->sandbox_process_info_.Receive()->hProcess = process; - target->base_address_ = base_address; - return target; -} - -} // namespace sandbox diff --git a/sandbox/src/target_process.h b/sandbox/src/target_process.h deleted file mode 100644 index 6e462c3..0000000 --- a/sandbox/src/target_process.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_TARGET_PROCESS_H__ -#define SANDBOX_SRC_TARGET_PROCESS_H__ - -#include - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/win/scoped_handle.h" -#include "base/win/scoped_process_information.h" -#include "sandbox/src/crosscall_server.h" -#include "sandbox/src/sandbox_types.h" - -namespace sandbox { - -class SharedMemIPCServer; -class ThreadProvider; - -// TargetProcess models a target instance (child process). Objects of this -// class are owned by the Policy used to create them. -class TargetProcess { - public: - // The constructor takes ownership of |initial_token| and |lockdown_token|. - TargetProcess(HANDLE initial_token, HANDLE lockdown_token, HANDLE job, - ThreadProvider* thread_pool); - ~TargetProcess(); - - // TODO(cpu): Currently there does not seem to be a reason to implement - // reference counting for this class since is internal, but kept the - // the same interface so the interception framework does not need to be - // touched at this point. - void AddRef() {} - void Release() {} - - // Creates the new target process. The process is created suspended. - DWORD Create(const wchar_t* exe_path, - const wchar_t* command_line, - const wchar_t* desktop, - base::win::ScopedProcessInformation* target_info); - - // Destroys the target process. - void Terminate(); - - // Creates the IPC objects such as the BrokerDispatcher and the - // IPC server. The IPC server uses the services of the thread_pool. - DWORD Init(Dispatcher* ipc_dispatcher, void* policy, - uint32 shared_IPC_size, uint32 shared_policy_size); - - // Returns the handle to the target process. - HANDLE Process() const { - return sandbox_process_info_.process_handle(); - } - - // Returns the handle to the job object that the target process belongs to. - HANDLE Job() const { - return job_; - } - - // Returns the address of the target main exe. This is used by the - // interceptions framework. - HMODULE MainModule() const { - return reinterpret_cast(base_address_); - } - - // Returns the name of the executable. - const wchar_t* Name() const { - return exe_name_.get(); - } - - // Returns the process id. - DWORD ProcessId() const { - return sandbox_process_info_.process_id(); - } - - // Returns the handle to the main thread. - HANDLE MainThread() const { - return sandbox_process_info_.thread_handle(); - } - - // Transfers a 32-bit variable between the broker and the target. - ResultCode TransferVariable(const char* name, void* address, size_t size); - - private: - // Details of the target process. - base::win::ScopedProcessInformation sandbox_process_info_; - // The token associated with the process. It provides the core of the - // sbox security. - base::win::ScopedHandle lockdown_token_; - // The token given to the initial thread so that the target process can - // start. It has more powers than the lockdown_token. - base::win::ScopedHandle initial_token_; - // Kernel handle to the shared memory used by the IPC server. - base::win::ScopedHandle shared_section_; - // Job object containing the target process. - HANDLE job_; - // Reference to the IPC subsystem. - scoped_ptr ipc_server_; - // Provides the threads used by the IPC. This class does not own this pointer. - ThreadProvider* thread_pool_; - // Base address of the main executable - void* base_address_; - // Full name of the target executable. - scoped_ptr_malloc exe_name_; - - // Function used for testing. - friend TargetProcess* MakeTestTargetProcess(HANDLE process, - HMODULE base_address); - - DISALLOW_IMPLICIT_CONSTRUCTORS(TargetProcess); -}; - -// Creates a mock TargetProcess used for testing interceptions. -// TODO(cpu): It seems that this method is not going to be used anymore. -TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address); - - -} // namespace sandbox - -#endif // SANDBOX_SRC_TARGET_PROCESS_H__ diff --git a/sandbox/src/target_services.cc b/sandbox/src/target_services.cc deleted file mode 100644 index e13a3d6..0000000 --- a/sandbox/src/target_services.cc +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/target_services.h" - -#include - -#include "base/basictypes.h" -#include "sandbox/src/crosscall_client.h" -#include "sandbox/src/handle_closer_agent.h" -#include "sandbox/src/handle_interception.h" -#include "sandbox/src/ipc_tags.h" -#include "sandbox/src/restricted_token_utils.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_types.h" -#include "sandbox/src/sharedmem_ipc_client.h" -#include "sandbox/src/sandbox_nt_util.h" - -namespace { - -// Flushing a cached key is triggered by just opening the key and closing the -// resulting handle. RegDisablePredefinedCache() is the documented way to flush -// HKCU so do not use it with this function. -bool FlushRegKey(HKEY root) { - HKEY key; - if (ERROR_SUCCESS == ::RegOpenKeyExW(root, NULL, 0, MAXIMUM_ALLOWED, &key)) { - if (ERROR_SUCCESS != ::RegCloseKey(key)) - return false; - } - return true; -} - -// This function forces advapi32.dll to release some internally cached handles -// that were made during calls to RegOpenkey and RegOpenKeyEx if it is called -// with a more restrictive token. Returns true if the flushing is succesful -// although this behavior is undocumented and there is no guarantee that in -// fact this will happen in future versions of windows. -bool FlushCachedRegHandles() { - return (FlushRegKey(HKEY_LOCAL_MACHINE) && - FlushRegKey(HKEY_CLASSES_ROOT) && - FlushRegKey(HKEY_USERS)); -} - -// Checks if we have handle entries pending and runs the closer. -bool CloseOpenHandles() { - if (sandbox::HandleCloserAgent::NeedsHandlesClosed()) { - sandbox::HandleCloserAgent handle_closer; - - handle_closer.InitializeHandlesToClose(); - if (!handle_closer.CloseHandles()) - return false; - } - - return true; -} - -} // namespace - -namespace sandbox { - -SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level = - INTEGRITY_LEVEL_LAST; - -TargetServicesBase::TargetServicesBase() { -} - -ResultCode TargetServicesBase::Init() { - process_state_.SetInitCalled(); - return SBOX_ALL_OK; -} - -// Failure here is a breach of security so the process is terminated. -void TargetServicesBase::LowerToken() { - if (ERROR_SUCCESS != - SetProcessIntegrityLevel(g_shared_delayed_integrity_level)) - ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_INTEGRITY); - process_state_.SetRevertedToSelf(); - // If the client code as called RegOpenKey, advapi32.dll has cached some - // handles. The following code gets rid of them. - if (!::RevertToSelf()) - ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_DROPTOKEN); - if (!FlushCachedRegHandles()) - ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES); - if (ERROR_SUCCESS != ::RegDisablePredefinedCache()) - ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE); - if (!CloseOpenHandles()) - ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES); -} - -ProcessState* TargetServicesBase::GetState() { - return &process_state_; -} - -TargetServicesBase* TargetServicesBase::GetInstance() { - static TargetServicesBase instance; - return &instance; -} - -// The broker services a 'test' IPC service with the IPC_PING_TAG tag. -bool TargetServicesBase::TestIPCPing(int version) { - void* memory = GetGlobalIPCMemory(); - if (NULL == memory) { - return false; - } - SharedMemIPCClient ipc(memory); - CrossCallReturn answer = {0}; - - if (1 == version) { - uint32 tick1 = ::GetTickCount(); - uint32 cookie = 717115; - ResultCode code = CrossCall(ipc, IPC_PING1_TAG, cookie, &answer); - - if (SBOX_ALL_OK != code) { - return false; - } - // We should get two extended returns values from the IPC, one is the - // tick count on the broker and the other is the cookie times two. - if ((answer.extended_count != 2)) { - return false; - } - // We test the first extended answer to be within the bounds of the tick - // count only if there was no tick count wraparound. - uint32 tick2 = ::GetTickCount(); - if (tick2 >= tick1) { - if ((answer.extended[0].unsigned_int < tick1) || - (answer.extended[0].unsigned_int > tick2)) { - return false; - } - } - - if (answer.extended[1].unsigned_int != cookie * 2) { - return false; - } - } else if (2 == version) { - uint32 cookie = 717111; - InOutCountedBuffer counted_buffer(&cookie, sizeof(cookie)); - ResultCode code = CrossCall(ipc, IPC_PING2_TAG, counted_buffer, &answer); - - if (SBOX_ALL_OK != code) { - return false; - } - if (cookie != 717111 * 3) { - return false; - } - } else { - return false; - } - return true; -} - -bool ProcessState::IsKernel32Loaded() { - return process_state_ != 0; -} - -bool ProcessState::InitCalled() { - return process_state_ > 1; -} - -bool ProcessState::RevertedToSelf() { - return process_state_ > 2; -} - -void ProcessState::SetKernel32Loaded() { - if (!process_state_) - process_state_ = 1; -} - -void ProcessState::SetInitCalled() { - if (process_state_ < 2) - process_state_ = 2; -} - -void ProcessState::SetRevertedToSelf() { - if (process_state_ < 3) - process_state_ = 3; -} - -ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle, - DWORD target_process_id, - HANDLE* target_handle, - DWORD desired_access, - DWORD options) { - return sandbox::DuplicateHandleProxy(source_handle, target_process_id, - target_handle, desired_access, options); -} - -} // namespace sandbox diff --git a/sandbox/src/target_services.h b/sandbox/src/target_services.h deleted file mode 100644 index c4bf4f6..0000000 --- a/sandbox/src/target_services.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_TARGET_SERVICES_H__ -#define SANDBOX_SRC_TARGET_SERVICES_H__ - -#include "base/basictypes.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/win_utils.h" - -namespace sandbox { - -class ProcessState { - public: - ProcessState() : process_state_(0) {} - - // Returns true if kernel32.dll has been loaded. - bool IsKernel32Loaded(); - - // Returns true if main has been called. - bool InitCalled(); - - // Returns true if LowerToken has been called. - bool RevertedToSelf(); - - // Set the current state. - void SetKernel32Loaded(); - void SetInitCalled(); - void SetRevertedToSelf(); - - public: - int process_state_; - DISALLOW_COPY_AND_ASSIGN(ProcessState); -}; - -// This class is an implementation of the TargetServices. -// Look in the documentation of sandbox::TargetServices for more info. -// Do NOT add a destructor to this class without changing the implementation of -// the factory method. -class TargetServicesBase : public TargetServices { - public: - TargetServicesBase(); - - // Public interface of TargetServices. - virtual ResultCode Init(); - virtual void LowerToken(); - virtual ProcessState* GetState(); - virtual ResultCode DuplicateHandle(HANDLE source_handle, - DWORD target_process_id, - HANDLE* target_handle, - DWORD desired_access, - DWORD options); - - // Factory method. - static TargetServicesBase* GetInstance(); - - // Sends a simple IPC Message that has a well-known answer. Returns true - // if the IPC was successful and false otherwise. There are 2 versions of - // this test: 1 and 2. The first one send a simple message while the - // second one send a message with an in/out param. - bool TestIPCPing(int version); - - private: - ProcessState process_state_; - DISALLOW_COPY_AND_ASSIGN(TargetServicesBase); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_TARGET_SERVICES_H__ diff --git a/sandbox/src/threadpool_unittest.cc b/sandbox/src/threadpool_unittest.cc deleted file mode 100644 index 2c85b57..0000000 --- a/sandbox/src/threadpool_unittest.cc +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/win2k_threadpool.h" -#include "testing/gtest/include/gtest/gtest.h" - -void __stdcall EmptyCallBack(void*, unsigned char) { -} - -void __stdcall TestCallBack(void* context, unsigned char) { - HANDLE event = reinterpret_cast(context); - ::SetEvent(event); -} - -namespace sandbox { - -// Test that register and unregister work, part 1. -TEST(IPCTest, ThreadPoolRegisterTest1) { - Win2kThreadPool thread_pool; - - EXPECT_EQ(0, thread_pool.OutstandingWaits()); - - HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL); - HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL); - - uint32 context = 0; - EXPECT_FALSE(thread_pool.RegisterWait(0, event1, EmptyCallBack, &context)); - EXPECT_EQ(0, thread_pool.OutstandingWaits()); - - EXPECT_TRUE(thread_pool.RegisterWait(this, event1, EmptyCallBack, &context)); - EXPECT_EQ(1, thread_pool.OutstandingWaits()); - EXPECT_TRUE(thread_pool.RegisterWait(this, event2, EmptyCallBack, &context)); - EXPECT_EQ(2, thread_pool.OutstandingWaits()); - - EXPECT_TRUE(thread_pool.UnRegisterWaits(this)); - EXPECT_EQ(0, thread_pool.OutstandingWaits()); - - EXPECT_EQ(TRUE, ::CloseHandle(event1)); - EXPECT_EQ(TRUE, ::CloseHandle(event2)); -} - -// Test that register and unregister work, part 2. -TEST(IPCTest, ThreadPoolRegisterTest2) { - Win2kThreadPool thread_pool; - - HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL); - HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL); - - uint32 context = 0; - uint32 c1 = 0; - uint32 c2 = 0; - - EXPECT_TRUE(thread_pool.RegisterWait(&c1, event1, EmptyCallBack, &context)); - EXPECT_EQ(1, thread_pool.OutstandingWaits()); - EXPECT_TRUE(thread_pool.RegisterWait(&c2, event2, EmptyCallBack, &context)); - EXPECT_EQ(2, thread_pool.OutstandingWaits()); - - EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2)); - EXPECT_EQ(1, thread_pool.OutstandingWaits()); - EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2)); - EXPECT_EQ(1, thread_pool.OutstandingWaits()); - - EXPECT_TRUE(thread_pool.UnRegisterWaits(&c1)); - EXPECT_EQ(0, thread_pool.OutstandingWaits()); - - EXPECT_EQ(TRUE, ::CloseHandle(event1)); - EXPECT_EQ(TRUE, ::CloseHandle(event2)); -} - -// Test that the thread pool has at least a thread that services an event. -// Test that when the event is un-registered is no longer serviced. -TEST(IPCTest, ThreadPoolSignalAndWaitTest) { - Win2kThreadPool thread_pool; - - // The events are auto reset and start not signaled. - HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL); - HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL); - - EXPECT_TRUE(thread_pool.RegisterWait(this, event1, TestCallBack, event2)); - - EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, FALSE)); - EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, FALSE)); - - EXPECT_TRUE(thread_pool.UnRegisterWaits(this)); - EXPECT_EQ(0, thread_pool.OutstandingWaits()); - - EXPECT_EQ(WAIT_TIMEOUT, ::SignalObjectAndWait(event1, event2, 1000, FALSE)); - - EXPECT_EQ(TRUE, ::CloseHandle(event1)); - EXPECT_EQ(TRUE, ::CloseHandle(event2)); -} - -} // namespace sandbox diff --git a/sandbox/src/unload_dll_test.cc b/sandbox/src/unload_dll_test.cc deleted file mode 100644 index 2bce1b8..0000000 --- a/sandbox/src/unload_dll_test.cc +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/win/scoped_handle.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/target_services.h" -#include "sandbox/tests/common/controller.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -// Loads and or unloads a DLL passed in the second parameter of argv. -// The first parameter of argv is 'L' = load, 'U' = unload or 'B' for both. -SBOX_TESTS_COMMAND int UseOneDLL(int argc, wchar_t **argv) { - if (argc != 2) - return SBOX_TEST_FAILED_TO_RUN_TEST; - int rv = SBOX_TEST_FAILED_TO_RUN_TEST; - - wchar_t option = (argv[0])[0]; - if ((option == L'L') || (option == L'B')) { - HMODULE module1 = ::LoadLibraryW(argv[1]); - rv = (module1 == NULL) ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED; - } - - if ((option == L'U') || (option == L'B')) { - HMODULE module2 = ::GetModuleHandleW(argv[1]); - rv = ::FreeLibrary(module2) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; - } - return rv; -} - -// Opens an event passed as the first parameter of argv. -SBOX_TESTS_COMMAND int SimpleOpenEvent(int argc, wchar_t **argv) { - if (argc != 1) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - base::win::ScopedHandle event_open(::OpenEvent(SYNCHRONIZE, FALSE, argv[0])); - return event_open.Get() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; -} - -// Flaky on windows, see http://crbug.com/80569. -TEST(UnloadDllTest, DISABLED_BaselineAvicapDll) { - TestRunner runner; - runner.SetTestState(BEFORE_REVERT); - runner.SetTimeout(2000); - // Add a sync rule, because that ensures that the interception agent has - // more than one item in its internal table. - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, - TargetPolicy::EVENTS_ALLOW_ANY, L"t0001")); - - // Note for the puzzled: avicap32.dll is a 64-bit dll in 64-bit versions of - // windows so this test and the others just work. - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL L avicap32.dll")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL B avicap32.dll")); -} - -// Flaky on windows, see http://crbug.com/80569. -TEST(UnloadDllTest, DISABLED_UnloadAviCapDllNoPatching) { - TestRunner runner; - runner.SetTestState(BEFORE_REVERT); - runner.SetTimeout(2000); - sandbox::TargetPolicy* policy = runner.GetPolicy(); - policy->AddDllToUnload(L"avicap32.dll"); - EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll")); - EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL B avicap32.dll")); -} - -// Flaky: http://crbug.com/38404 -TEST(UnloadDllTest, DISABLED_UnloadAviCapDllWithPatching) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(BEFORE_REVERT); - sandbox::TargetPolicy* policy = runner.GetPolicy(); - policy->AddDllToUnload(L"avicap32.dll"); - - base::win::ScopedHandle handle1(::CreateEvent( - NULL, FALSE, FALSE, L"tst0001")); - - // Add a couple of rules that ensures that the interception agent add EAT - // patching on the client which makes sure that the unload dll record does - // not interact badly with them. - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, - TargetPolicy::REG_ALLOW_ANY, - L"HKEY_LOCAL_MACHINE\\Software\\Microsoft")); - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, - TargetPolicy::EVENTS_ALLOW_ANY, L"tst0001")); - - EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll")); - - runner.SetTestState(AFTER_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"SimpleOpenEvent tst0001")); -} - -} // namespace sandbox diff --git a/sandbox/src/win2k_threadpool.cc b/sandbox/src/win2k_threadpool.cc deleted file mode 100644 index fe2473e..0000000 --- a/sandbox/src/win2k_threadpool.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/win2k_threadpool.h" - -#include "sandbox/src/win_utils.h" - -namespace sandbox { - -bool Win2kThreadPool::RegisterWait(const void* cookie, HANDLE waitable_object, - CrossCallIPCCallback callback, - void* context) { - if (0 == cookie) { - return false; - } - HANDLE pool_object = NULL; - // create a wait for a kernel object, with no timeout - if (!::RegisterWaitForSingleObject(&pool_object, waitable_object, callback, - context, INFINITE, WT_EXECUTEDEFAULT)) { - return false; - } - PoolObject pool_obj = {cookie, pool_object}; - AutoLock lock(&lock_); - pool_objects_.push_back(pool_obj); - return true; -} - -bool Win2kThreadPool::UnRegisterWaits(void* cookie) { - if (0 == cookie) { - return false; - } - AutoLock lock(&lock_); - bool success = true; - PoolObjects::iterator it = pool_objects_.begin(); - while (it != pool_objects_.end()) { - if (it->cookie == cookie) { - HANDLE wait = it->wait; - it = pool_objects_.erase(it); - success &= (::UnregisterWaitEx(wait, INVALID_HANDLE_VALUE) != 0); - } else { - ++it; - } - } - return success; -} - -size_t Win2kThreadPool::OutstandingWaits() { - AutoLock lock(&lock_); - return pool_objects_.size(); -} - -Win2kThreadPool::~Win2kThreadPool() { - // Here we used to unregister all the pool wait handles. Now, following the - // rest of the code we avoid lengthy or blocking calls given that the process - // is being torn down. - ::DeleteCriticalSection(&lock_); -} - -} // namespace sandbox diff --git a/sandbox/src/win2k_threadpool.h b/sandbox/src/win2k_threadpool.h deleted file mode 100644 index 8593fa3..0000000 --- a/sandbox/src/win2k_threadpool.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_WIN2K_THREADPOOL_H_ -#define SANDBOX_SRC_WIN2K_THREADPOOL_H_ - -#include -#include -#include "sandbox/src/crosscall_server.h" - -namespace sandbox { - -// Win2kThreadPool a simple implementation of a thread provider as required -// for the sandbox IPC subsystem. See sandbox\crosscall_server.h for the details -// and requirements of this interface. -// -// Implementing the thread provider as a thread pool is desirable in the case -// of shared memory IPC because it can generate a large number of waitable -// events: as many as channels. A thread pool does not create a thread per -// event, instead maintains a few idle threads but can create more if the need -// arises. -// -// This implementation simply thunks to the nice thread pool API of win2k. -class Win2kThreadPool : public ThreadProvider { - public: - Win2kThreadPool() { - ::InitializeCriticalSection(&lock_); - } - virtual ~Win2kThreadPool(); - - virtual bool RegisterWait(const void* cookie, HANDLE waitable_object, - CrossCallIPCCallback callback, - void* context); - - virtual bool UnRegisterWaits(void* cookie); - - // Returns the total number of wait objects associated with - // the thread pool. - size_t OutstandingWaits(); - - private: - // record to keep track of a wait and its associated cookie. - struct PoolObject { - const void* cookie; - HANDLE wait; - }; - // The list of pool wait objects. - typedef std::list PoolObjects; - PoolObjects pool_objects_; - // This lock protects the list of pool wait objects. - CRITICAL_SECTION lock_; - DISALLOW_COPY_AND_ASSIGN(Win2kThreadPool); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_WIN2K_THREADPOOL_H_ diff --git a/sandbox/src/win_utils.cc b/sandbox/src/win_utils.cc deleted file mode 100644 index 8a43d97..0000000 --- a/sandbox/src/win_utils.cc +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/win_utils.h" - -#include - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "sandbox/src/internal_types.h" -#include "sandbox/src/nt_internals.h" - -namespace { - -// Holds the information about a known registry key. -struct KnownReservedKey { - const wchar_t* name; - HKEY key; -}; - -// Contains all the known registry key by name and by handle. -const KnownReservedKey kKnownKey[] = { - { L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT }, - { L"HKEY_CURRENT_USER", HKEY_CURRENT_USER }, - { L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE}, - { L"HKEY_USERS", HKEY_USERS}, - { L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA}, - { L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT}, - { L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT}, - { L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG}, - { L"HKEY_DYN_DATA", HKEY_DYN_DATA} -}; - -// Returns true if the provided path points to a pipe. -bool IsPipe(const std::wstring& path) { - size_t start = 0; - if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix)) - start = sandbox::kNTPrefixLen; - - const wchar_t kPipe[] = L"pipe\\"; - return (0 == path.compare(start, arraysize(kPipe) - 1, kPipe)); -} - -} // namespace - -namespace sandbox { - -HKEY GetReservedKeyFromName(const std::wstring& name) { - for (size_t i = 0; i < arraysize(kKnownKey); ++i) { - if (name == kKnownKey[i].name) - return kKnownKey[i].key; - } - - return NULL; -} - -bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name) { - for (size_t i = 0; i < arraysize(kKnownKey); ++i) { - if (name.find(kKnownKey[i].name) == 0) { - HKEY key; - DWORD disposition; - if (ERROR_SUCCESS != ::RegCreateKeyEx(kKnownKey[i].key, L"", 0, NULL, 0, - MAXIMUM_ALLOWED, NULL, &key, - &disposition)) - return false; - - bool result = GetPathFromHandle(key, resolved_name); - ::RegCloseKey(key); - - if (!result) - return false; - - *resolved_name += name.substr(wcslen(kKnownKey[i].name)); - return true; - } - } - - return false; -} - -DWORD IsReparsePoint(const std::wstring& full_path, bool* result) { - std::wstring path = full_path; - - // Remove the nt prefix. - if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) - path = path.substr(kNTPrefixLen); - - // Check if it's a pipe. We can't query the attributes of a pipe. - if (IsPipe(path)) { - *result = FALSE; - return ERROR_SUCCESS; - } - - std::wstring::size_type last_pos = std::wstring::npos; - - do { - path = path.substr(0, last_pos); - - DWORD attributes = ::GetFileAttributes(path.c_str()); - if (INVALID_FILE_ATTRIBUTES == attributes) { - DWORD error = ::GetLastError(); - if (error != ERROR_FILE_NOT_FOUND && - error != ERROR_PATH_NOT_FOUND && - error != ERROR_INVALID_NAME) { - // Unexpected error. - NOTREACHED(); - return error; - } - } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) { - // This is a reparse point. - *result = true; - return ERROR_SUCCESS; - } - - last_pos = path.rfind(L'\\'); - } while (last_pos != std::wstring::npos); - - *result = false; - return ERROR_SUCCESS; -} - -// We get a |full_path| of the form \??\c:\some\foo\bar, and the name that -// we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar. -bool SameObject(HANDLE handle, const wchar_t* full_path) { - std::wstring path(full_path); - DCHECK(!path.empty()); - - // Check if it's a pipe. - if (IsPipe(path)) - return true; - - std::wstring actual_path; - if (!GetPathFromHandle(handle, &actual_path)) - return false; - - // This may end with a backslash. - const wchar_t kBackslash = '\\'; - if (path[path.length() - 1] == kBackslash) - path = path.substr(0, path.length() - 1); - - // Perfect match (case-insesitive check). - if (0 == _wcsicmp(actual_path.c_str(), path.c_str())) - return true; - - // Look for the drive letter. - size_t colon_pos = path.find(L':'); - if (colon_pos == 0 || colon_pos == std::wstring::npos) - return false; - - // Only one character for the drive. - if (colon_pos > 1 && path[colon_pos - 2] != kBackslash) - return false; - - // We only need 3 chars, but let's alloc a buffer for four. - wchar_t drive[4] = {0}; - wchar_t vol_name[MAX_PATH]; - memcpy(drive, &path[colon_pos - 1], 2 * sizeof(*drive)); - - // We'll get a double null terminated string. - DWORD vol_length = ::QueryDosDeviceW(drive, vol_name, MAX_PATH); - if (vol_length < 2 || vol_length == MAX_PATH) - return false; - - // Ignore the nulls at the end. - vol_length = static_cast(wcslen(vol_name)); - - // The two paths should be the same length. - if (vol_length + path.size() - (colon_pos + 1) != actual_path.size()) - return false; - - // Check up to the drive letter. - if (0 != _wcsnicmp(actual_path.c_str(), vol_name, vol_length)) - return false; - - // Check the path after the drive letter. - if (0 != _wcsicmp(&actual_path[vol_length], &path[colon_pos + 1])) - return false; - - return true; -} - -bool ConvertToLongPath(const std::wstring& short_path, - std::wstring* long_path) { - // Check if the path is a NT path. - bool is_nt_path = false; - std::wstring path = short_path; - if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) { - path = path.substr(kNTPrefixLen); - is_nt_path = true; - } - - DWORD size = MAX_PATH; - scoped_array long_path_buf(new wchar_t[size]); - - DWORD return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(), - size); - while (return_value >= size) { - size *= 2; - long_path_buf.reset(new wchar_t[size]); - return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(), size); - } - - DWORD last_error = ::GetLastError(); - if (0 == return_value && (ERROR_FILE_NOT_FOUND == last_error || - ERROR_PATH_NOT_FOUND == last_error || - ERROR_INVALID_NAME == last_error)) { - // The file does not exist, but maybe a sub path needs to be expanded. - std::wstring::size_type last_slash = path.rfind(L'\\'); - if (std::wstring::npos == last_slash) - return false; - - std::wstring begin = path.substr(0, last_slash); - std::wstring end = path.substr(last_slash); - if (!ConvertToLongPath(begin, &begin)) - return false; - - // Ok, it worked. Let's reset the return value. - path = begin + end; - return_value = 1; - } else if (0 != return_value) { - path = long_path_buf.get(); - } - - if (return_value != 0) { - if (is_nt_path) { - *long_path = kNTPrefix; - *long_path += path; - } else { - *long_path = path; - } - - return true; - } - - return false; -} - -bool GetPathFromHandle(HANDLE handle, std::wstring* path) { - NtQueryObjectFunction NtQueryObject = NULL; - ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject); - - OBJECT_NAME_INFORMATION initial_buffer; - OBJECT_NAME_INFORMATION* name = &initial_buffer; - ULONG size = sizeof(initial_buffer); - // Query the name information a first time to get the size of the name. - NTSTATUS status = NtQueryObject(handle, ObjectNameInformation, name, size, - &size); - - scoped_ptr name_ptr; - if (size) { - name = reinterpret_cast(new BYTE[size]); - name_ptr.reset(name); - - // Query the name information a second time to get the name of the - // object referenced by the handle. - status = NtQueryObject(handle, ObjectNameInformation, name, size, &size); - } - - if (STATUS_SUCCESS != status) - return false; - - path->assign(name->ObjectName.Buffer, name->ObjectName.Length / - sizeof(name->ObjectName.Buffer[0])); - return true; -} - -bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path) { - HANDLE file = ::CreateFileW(path.c_str(), 0, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (file == INVALID_HANDLE_VALUE) - return false; - bool rv = GetPathFromHandle(file, nt_path); - ::CloseHandle(file); - return rv; -} - -bool WriteProtectedChildMemory(HANDLE child_process, void* address, - const void* buffer, size_t length) { - // First, remove the protections. - DWORD old_protection; - if (!::VirtualProtectEx(child_process, address, length, - PAGE_WRITECOPY, &old_protection)) - return false; - - SIZE_T written; - bool ok = ::WriteProcessMemory(child_process, address, buffer, length, - &written) && (length == written); - - // Always attempt to restore the original protection. - if (!::VirtualProtectEx(child_process, address, length, - old_protection, &old_protection)) - return false; - - return ok; -} - -}; // namespace sandbox - -// TODO(jschuh): http://crbug.com/11789 -// I'm guessing we have a race where some "security" software is messing -// with ntdll/imports underneath us. So, we retry a few times, and in the -// worst case we sleep briefly before a few more attempts. (Normally sleeping -// would be very bad, but it's better than crashing in this case.) -void ResolveNTFunctionPtr(const char* name, void* ptr) { - const int max_tries = 5; - const int sleep_threshold = 2; - - static HMODULE ntdll = ::GetModuleHandle(sandbox::kNtdllName); - - FARPROC* function_ptr = reinterpret_cast(ptr); - *function_ptr = ::GetProcAddress(ntdll, name); - - for (int tries = 1; !(*function_ptr) && tries < max_tries; ++tries) { - if (tries >= sleep_threshold) - ::Sleep(1); - ntdll = ::GetModuleHandle(sandbox::kNtdllName); - *function_ptr = ::GetProcAddress(ntdll, name); - } - - CHECK(*function_ptr); -} diff --git a/sandbox/src/win_utils.h b/sandbox/src/win_utils.h deleted file mode 100644 index a80bb81..0000000 --- a/sandbox/src/win_utils.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_WIN_UTILS_H_ -#define SANDBOX_SRC_WIN_UTILS_H_ - -#include -#include -#include "base/basictypes.h" - -namespace sandbox { - -// Prefix for path used by NT calls. -const wchar_t kNTPrefix[] = L"\\??\\"; -const size_t kNTPrefixLen = arraysize(kNTPrefix) - 1; - -const wchar_t kNTObjManPrefix[] = L"\\Device\\"; -const size_t kNTObjManPrefixLen = arraysize(kNTObjManPrefix) - 1; - -// Automatically acquires and releases a lock when the object is -// is destroyed. -class AutoLock { - public: - // Acquires the lock. - explicit AutoLock(CRITICAL_SECTION *lock) : lock_(lock) { - ::EnterCriticalSection(lock); - }; - - // Releases the lock; - ~AutoLock() { - ::LeaveCriticalSection(lock_); - }; - - private: - CRITICAL_SECTION *lock_; - DISALLOW_IMPLICIT_CONSTRUCTORS(AutoLock); -}; - -// Basic implementation of a singleton which calls the destructor -// when the exe is shutting down or the DLL is being unloaded. -template -class SingletonBase { - public: - static Derived* GetInstance() { - static Derived* instance = NULL; - if (NULL == instance) { - instance = new Derived(); - // Microsoft CRT extension. In an exe this this called after - // winmain returns, in a dll is called in DLL_PROCESS_DETACH - _onexit(OnExit); - } - return instance; - } - - private: - // this is the function that gets called by the CRT when the - // process is shutting down. - static int __cdecl OnExit() { - delete GetInstance(); - return 0; - } -}; - -// Convert a short path (C:\path~1 or \\??\\c:\path~1) to the long version of -// the path. If the path is not a valid filesystem path, the function returns -// false and the output parameter is not modified. -bool ConvertToLongPath(const std::wstring& short_path, std::wstring* long_path); - -// Sets result to true if the path contains a reparse point. The return value -// is ERROR_SUCCESS when the function succeeds or the appropriate error code -// when the function fails. -// This function is not smart. It looks for each element in the path and -// returns true if any of them is a reparse point. -DWORD IsReparsePoint(const std::wstring& full_path, bool* result); - -// Returns true if the handle corresponds to the object pointed by this path. -bool SameObject(HANDLE handle, const wchar_t* full_path); - -// Resolves a handle to an nt path. Returns true if the handle can be resolved. -bool GetPathFromHandle(HANDLE handle, std::wstring* path); - -// Resolves a win32 path to an nt path using GetPathFromHandle. The path must -// exist. Returs true if the translation was succesful. -bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path); - -// Translates a reserved key name to its handle. -// For example "HKEY_LOCAL_MACHINE" returns HKEY_LOCAL_MACHINE. -// Returns NULL if the name does not represent any reserved key name. -HKEY GetReservedKeyFromName(const std::wstring& name); - -// Resolves a user-readable registry path to a system-readable registry path. -// For example, HKEY_LOCAL_MACHINE\\Software\\microsoft is translated to -// \\registry\\machine\\software\\microsoft. Returns false if the path -// cannot be resolved. -bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name); - -// Writes |length| bytes from the provided |buffer| into the address space of -// |child_process|, at the specified |address|, preserving the original write -// protection attributes. Returns true on success. -bool WriteProtectedChildMemory(HANDLE child_process, void* address, - const void* buffer, size_t length); - -} // namespace sandbox - -// Resolves a function name in NTDLL to a function pointer. The second parameter -// is a pointer to the function pointer. -void ResolveNTFunctionPtr(const char* name, void* ptr); - -#endif // SANDBOX_SRC_WIN_UTILS_H_ diff --git a/sandbox/src/win_utils_unittest.cc b/sandbox/src/win_utils_unittest.cc deleted file mode 100644 index 0f445ef..0000000 --- a/sandbox/src/win_utils_unittest.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "base/win/scoped_handle.h" -#include "sandbox/src/win_utils.h" -#include "sandbox/tests/common/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -TEST(WinUtils, IsReparsePoint) { - using sandbox::IsReparsePoint; - - // Create a temp file because we need write access to it. - wchar_t temp_directory[MAX_PATH]; - wchar_t my_folder[MAX_PATH]; - ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u); - - // Delete the file and create a directory instead. - ASSERT_TRUE(::DeleteFile(my_folder)); - ASSERT_TRUE(::CreateDirectory(my_folder, NULL)); - - bool result = true; - EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(my_folder, &result)); - EXPECT_FALSE(result); - - // We have to fix Bug 32224 to pass this test. - std::wstring not_found = std::wstring(my_folder) + L"\\foo\\bar"; - // EXPECT_EQ(ERROR_PATH_NOT_FOUND, IsReparsePoint(not_found, &result)); - - std::wstring new_file = std::wstring(my_folder) + L"\\foo"; - EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result)); - EXPECT_FALSE(result); - - // Replace the directory with a reparse point to %temp%. - HANDLE dir = ::CreateFile(my_folder, FILE_ALL_ACCESS, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - EXPECT_NE(INVALID_HANDLE_VALUE, dir); - - std::wstring temp_dir_nt = std::wstring(L"\\??\\") + temp_directory; - EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); - - EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result)); - EXPECT_TRUE(result); - - EXPECT_TRUE(DeleteReparsePoint(dir)); - EXPECT_TRUE(::CloseHandle(dir)); - EXPECT_TRUE(::RemoveDirectory(my_folder)); -} - -TEST(WinUtils, SameObject) { - using sandbox::SameObject; - - // Create a temp file because we need write access to it. - wchar_t temp_directory[MAX_PATH]; - wchar_t my_folder[MAX_PATH]; - ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u); - - // Delete the file and create a directory instead. - ASSERT_TRUE(::DeleteFile(my_folder)); - ASSERT_TRUE(::CreateDirectory(my_folder, NULL)); - - std::wstring folder(my_folder); - std::wstring file_name = folder + L"\\foo.txt"; - const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE; - base::win::ScopedHandle file(CreateFile( - file_name.c_str(), GENERIC_WRITE, kSharing, NULL, CREATE_ALWAYS, - FILE_FLAG_DELETE_ON_CLOSE, NULL)); - - EXPECT_TRUE(file.IsValid()); - std::wstring file_name_nt1 = std::wstring(L"\\??\\") + file_name; - std::wstring file_name_nt2 = std::wstring(L"\\??\\") + folder + L"\\FOO.txT"; - EXPECT_TRUE(SameObject(file.Get(), file_name_nt1.c_str())); - EXPECT_TRUE(SameObject(file.Get(), file_name_nt2.c_str())); - - file.Close(); - EXPECT_TRUE(::RemoveDirectory(my_folder)); -} diff --git a/sandbox/src/window.cc b/sandbox/src/window.cc deleted file mode 100644 index 507ae57..0000000 --- a/sandbox/src/window.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/window.h" - -#include - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" - -namespace { - -// Gets the security attributes of a window object referenced by |handle|. The -// lpSecurityDescriptor member of the SECURITY_ATTRIBUTES parameter returned -// must be freed using LocalFree by the caller. -bool GetSecurityAttributes(HANDLE handle, SECURITY_ATTRIBUTES* attributes) { - attributes->bInheritHandle = FALSE; - attributes->nLength = sizeof(SECURITY_ATTRIBUTES); - - PACL dacl = NULL; - DWORD result = ::GetSecurityInfo(handle, SE_WINDOW_OBJECT, - DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, - NULL, &attributes->lpSecurityDescriptor); - if (ERROR_SUCCESS == result) - return true; - - return false; -} - -} - -namespace sandbox { - -ResultCode CreateAltWindowStation(HWINSTA* winsta) { - // Get the security attributes from the current window station; we will - // use this as the base security attributes for the new window station. - SECURITY_ATTRIBUTES attributes = {0}; - if (!GetSecurityAttributes(::GetProcessWindowStation(), &attributes)) { - return SBOX_ERROR_CANNOT_CREATE_WINSTATION; - } - - // Create the window station using NULL for the name to ask the os to - // generate it. - // TODO(nsylvain): don't ask for WINSTA_ALL_ACCESS if we don't need to. - *winsta = ::CreateWindowStationW(NULL, 0, WINSTA_ALL_ACCESS, &attributes); - LocalFree(attributes.lpSecurityDescriptor); - - if (*winsta) - return SBOX_ALL_OK; - - return SBOX_ERROR_CANNOT_CREATE_WINSTATION; -} - -ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) { - std::wstring desktop_name = L"sbox_alternate_desktop_"; - - // Append the current PID to the desktop name. - wchar_t buffer[16]; - _snwprintf_s(buffer, sizeof(buffer) / sizeof(wchar_t), L"0x%X", - ::GetCurrentProcessId()); - desktop_name += buffer; - - // Get the security attributes from the current desktop, we will use this as - // the base security attributes for the new desktop. - SECURITY_ATTRIBUTES attributes = {0}; - if (!GetSecurityAttributes(GetThreadDesktop(GetCurrentThreadId()), - &attributes)) { - return SBOX_ERROR_CANNOT_CREATE_DESKTOP; - } - - // Back up the current window station, in case we need to switch it. - HWINSTA current_winsta = ::GetProcessWindowStation(); - - if (winsta) { - // We need to switch to the alternate window station before creating the - // desktop. - if (!::SetProcessWindowStation(winsta)) { - ::LocalFree(attributes.lpSecurityDescriptor); - return SBOX_ERROR_CANNOT_CREATE_DESKTOP; - } - } - - // Create the destkop. - // TODO(nsylvain): don't ask for GENERIC_ALL if we don't need to. - *desktop = ::CreateDesktop(desktop_name.c_str(), NULL, NULL, 0, GENERIC_ALL, - &attributes); - ::LocalFree(attributes.lpSecurityDescriptor); - - if (winsta) { - // Revert to the right window station. - if (!::SetProcessWindowStation(current_winsta)) { - return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION; - } - } - - if (*desktop) - return SBOX_ALL_OK; - - return SBOX_ERROR_CANNOT_CREATE_DESKTOP; -} - -std::wstring GetWindowObjectName(HANDLE handle) { - // Get the size of the name. - DWORD size = 0; - ::GetUserObjectInformation(handle, UOI_NAME, NULL, 0, &size); - - if (!size) { - NOTREACHED(); - return std::wstring(); - } - - // Create the buffer that will hold the name. - scoped_array name_buffer(new wchar_t[size]); - - // Query the name of the object. - if (!::GetUserObjectInformation(handle, UOI_NAME, name_buffer.get(), size, - &size)) { - NOTREACHED(); - return std::wstring(); - } - - return std::wstring(name_buffer.get()); -} - -std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop) { - if (!desktop) { - NOTREACHED(); - return std::wstring(); - } - - std::wstring name; - if (winsta) { - name = GetWindowObjectName(winsta); - name += L'\\'; - } - - name += GetWindowObjectName(desktop); - return name; -} - -} // namespace sandbox diff --git a/sandbox/src/window.h b/sandbox/src/window.h deleted file mode 100644 index f65b463..0000000 --- a/sandbox/src/window.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_SRC_WINDOW_H_ -#define SANDBOX_SRC_WINDOW_H_ - -#include -#include - -#include "sandbox/src/sandbox_types.h" - -namespace sandbox { - - // Creates a window station. The name is generated by the OS. The security - // descriptor is based on the security descriptor of the current window - // station. - ResultCode CreateAltWindowStation(HWINSTA* winsta); - - // Creates a desktop. The name is a static string followed by the pid of the - // current process. The security descriptor on the new desktop is based on the - // security descriptor of the desktop associated with the current thread. - // If a winsta is specified, the function will switch to it before creating - // the desktop. If the functions fails the switch back to the current winsta, - // the function will return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION. - ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop); - - // Returns the name of a desktop or a window station. - std::wstring GetWindowObjectName(HANDLE handle); - - // Returns the name of the desktop referenced by |desktop|. If a window - // station is specified, the name is prepended with the window station name, - // followed by a backslash. This name can be used as the lpDesktop parameter - // to CreateProcess. - std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop); - -} // namespace sandbox - -#endif // SANDBOX_SRC_WINDOW_H_ diff --git a/sandbox/tests/common/controller.cc b/sandbox/tests/common/controller.cc deleted file mode 100644 index 6c73c2d..0000000 --- a/sandbox/tests/common/controller.cc +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/tests/common/controller.h" - -#include - -#include "base/process.h" -#include "base/process_util.h" -#include "base/sys_string_conversions.h" -#include "base/win/windows_version.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/sandbox_utils.h" - -namespace { - -static const int kDefaultTimeout = 3000; - -// Constructs a full path to a file inside the system32 folder. -std::wstring MakePathToSys32(const wchar_t* name, bool is_obj_man_path) { - wchar_t windows_path[MAX_PATH] = {0}; - if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH)) - return std::wstring(); - - std::wstring full_path(windows_path); - if (full_path.empty()) - return full_path; - - if (is_obj_man_path) - full_path.insert(0, L"\\??\\"); - - full_path += L"\\system32\\"; - full_path += name; - return full_path; -} - -// Constructs a full path to a file inside the syswow64 folder. -std::wstring MakePathToSysWow64(const wchar_t* name, bool is_obj_man_path) { - wchar_t windows_path[MAX_PATH] = {0}; - if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH)) - return std::wstring(); - - std::wstring full_path(windows_path); - if (full_path.empty()) - return full_path; - - if (is_obj_man_path) - full_path.insert(0, L"\\??\\"); - - full_path += L"\\SysWOW64\\"; - full_path += name; - return full_path; -} - -bool IsProcessRunning(HANDLE process) { - DWORD exit_code = 0; - if (::GetExitCodeProcess(process, &exit_code)) - return exit_code == STILL_ACTIVE; - return false; -} - -} // namespace - -namespace sandbox { - -std::wstring MakePathToSys(const wchar_t* name, bool is_obj_man_path) { - return (base::win::OSInfo::GetInstance()->wow64_status() == - base::win::OSInfo::WOW64_ENABLED) ? - MakePathToSysWow64(name, is_obj_man_path) : - MakePathToSys32(name, is_obj_man_path); -} - -BrokerServices* GetBroker() { - static BrokerServices* broker = SandboxFactory::GetBrokerServices(); - static bool is_initialized = false; - - if (!broker) { - return NULL; - } - - if (!is_initialized) { - if (SBOX_ALL_OK != broker->Init()) - return NULL; - - is_initialized = true; - } - - return broker; -} - -TestRunner::TestRunner(JobLevel job_level, TokenLevel startup_token, - TokenLevel main_token) - : is_init_(false), is_async_(false), no_sandbox_(false), - target_process_id_(0) { - Init(job_level, startup_token, main_token); -} - -TestRunner::TestRunner() - : is_init_(false), is_async_(false), no_sandbox_(false), - target_process_id_(0) { - Init(JOB_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN); -} - -void TestRunner::Init(JobLevel job_level, TokenLevel startup_token, - TokenLevel main_token) { - broker_ = NULL; - policy_ = NULL; - timeout_ = kDefaultTimeout; - state_ = AFTER_REVERT; - is_async_= false; - target_process_id_ = 0; - - broker_ = GetBroker(); - if (!broker_) - return; - - policy_ = broker_->CreatePolicy(); - if (!policy_) - return; - - policy_->SetJobLevel(job_level, 0); - policy_->SetTokenLevel(startup_token, main_token); - - is_init_ = true; -} - -TargetPolicy* TestRunner::GetPolicy() { - return policy_; -} - -TestRunner::~TestRunner() { - if (target_process_) - ::TerminateProcess(target_process_, 0); - - if (policy_) - policy_->Release(); -} - -bool TestRunner::AddRule(TargetPolicy::SubSystem subsystem, - TargetPolicy::Semantics semantics, - const wchar_t* pattern) { - if (!is_init_) - return false; - - return (SBOX_ALL_OK == policy_->AddRule(subsystem, semantics, pattern)); -} - -bool TestRunner::AddRuleSys32(TargetPolicy::Semantics semantics, - const wchar_t* pattern) { - if (!is_init_) - return false; - - std::wstring win32_path = MakePathToSys32(pattern, false); - if (win32_path.empty()) - return false; - - if (!AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str())) - return false; - - if (base::win::OSInfo::GetInstance()->wow64_status() != - base::win::OSInfo::WOW64_ENABLED) - return true; - - win32_path = MakePathToSysWow64(pattern, false); - if (win32_path.empty()) - return false; - - return AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str()); -} - -bool TestRunner::AddFsRule(TargetPolicy::Semantics semantics, - const wchar_t* pattern) { - if (!is_init_) - return false; - - return AddRule(TargetPolicy::SUBSYS_FILES, semantics, pattern); -} - -int TestRunner::RunTest(const wchar_t* command) { - if (MAX_STATE > 10) - return SBOX_TEST_INVALID_PARAMETER; - - wchar_t state_number[2]; - state_number[0] = L'0' + state_; - state_number[1] = L'\0'; - std::wstring full_command(state_number); - full_command += L" "; - full_command += command; - - return InternalRunTest(full_command.c_str()); -} - -int TestRunner::InternalRunTest(const wchar_t* command) { - if (!is_init_) - return SBOX_TEST_FAILED_TO_RUN_TEST; - - // For simplicity TestRunner supports only one process per instance. - if (target_process_) { - if (IsProcessRunning(target_process_)) - return SBOX_TEST_FAILED_TO_RUN_TEST; - target_process_.Close(); - target_process_id_ = 0; - } - - // Get the path to the sandboxed process. - wchar_t prog_name[MAX_PATH]; - GetModuleFileNameW(NULL, prog_name, MAX_PATH); - - // Launch the sandboxed process. - ResultCode result = SBOX_ALL_OK; - PROCESS_INFORMATION target = {0}; - - std::wstring arguments(L"\""); - arguments += prog_name; - arguments += L"\" -child"; - arguments += no_sandbox_ ? L"-no-sandbox " : L" "; - arguments += command; - - if (no_sandbox_) { - STARTUPINFO startup_info = {sizeof(STARTUPINFO)}; - if (!::CreateProcessW(prog_name, &arguments[0], NULL, NULL, FALSE, 0, - NULL, NULL, &startup_info, &target)) { - return SBOX_ERROR_GENERIC; - } - broker_->AddTargetPeer(target.hProcess); - } else { - result = broker_->SpawnTarget(prog_name, arguments.c_str(), policy_, - &target); - } - - if (SBOX_ALL_OK != result) - return SBOX_TEST_FAILED_TO_RUN_TEST; - - ::ResumeThread(target.hThread); - - // For an asynchronous run we don't bother waiting. - if (is_async_) { - target_process_.Set(target.hProcess); - target_process_id_ = target.dwProcessId; - ::CloseHandle(target.hThread); - return SBOX_TEST_SUCCEEDED; - } - - if (::IsDebuggerPresent()) { - // Don't kill the target process on a time-out while we are debugging. - timeout_ = INFINITE; - } - - if (WAIT_TIMEOUT == ::WaitForSingleObject(target.hProcess, timeout_)) { - ::TerminateProcess(target.hProcess, SBOX_TEST_TIMED_OUT); - ::CloseHandle(target.hProcess); - ::CloseHandle(target.hThread); - return SBOX_TEST_TIMED_OUT; - } - - DWORD exit_code = SBOX_TEST_LAST_RESULT; - if (!::GetExitCodeProcess(target.hProcess, &exit_code)) { - ::CloseHandle(target.hProcess); - ::CloseHandle(target.hThread); - return SBOX_TEST_FAILED_TO_RUN_TEST; - } - - ::CloseHandle(target.hProcess); - ::CloseHandle(target.hThread); - - return exit_code; -} - -void TestRunner::SetTimeout(DWORD timeout_ms) { - timeout_ = timeout_ms; -} - -void TestRunner::SetTestState(SboxTestsState desired_state) { - state_ = desired_state; -} - -// This is the main procedure for the target (child) application. We'll find out -// the target test and call it. -// We expect the arguments to be: -// argv[1] = "-child" -// argv[2] = SboxTestsState when to run the command -// argv[3] = command to run -// argv[4...] = command arguments. -int DispatchCall(int argc, wchar_t **argv) { - if (argc < 4) - return SBOX_TEST_INVALID_PARAMETER; - - // We hard code two tests to avoid dispatch failures. - if (0 == _wcsicmp(argv[3], L"wait")) { - Sleep(INFINITE); - return SBOX_TEST_TIMED_OUT; - } - - if (0 == _wcsicmp(argv[3], L"ping")) - return SBOX_TEST_PING_OK; - - SboxTestsState state = static_cast(_wtoi(argv[2])); - if ((state <= MIN_STATE) || (state >= MAX_STATE)) - return SBOX_TEST_INVALID_PARAMETER; - - HMODULE module; - if (!GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - reinterpret_cast(&DispatchCall), - &module)) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - std::string command_name = base::SysWideToMultiByte(argv[3], CP_UTF8); - CommandFunction command = reinterpret_cast( - ::GetProcAddress(module, command_name.c_str())); - if (!command) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - if (BEFORE_INIT == state) - return command(argc - 4, argv + 4); - else if (EVERY_STATE == state) - command(argc - 4, argv + 4); - - TargetServices* target = SandboxFactory::GetTargetServices(); - if (target) { - if (SBOX_ALL_OK != target->Init()) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - if (BEFORE_REVERT == state) - return command(argc - 4, argv + 4); - else if (EVERY_STATE == state) - command(argc - 4, argv + 4); - - target->LowerToken(); - } else if (0 != _wcsicmp(argv[1], L"-child-no-sandbox")) { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - - return command(argc - 4, argv + 4); -} - -} // namespace sandbox diff --git a/sandbox/tests/common/controller.h b/sandbox/tests/common/controller.h deleted file mode 100644 index 5c2a471..0000000 --- a/sandbox/tests/common/controller.h +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_TESTS_COMMON_CONTROLLER_H_ -#define SANDBOX_TESTS_COMMON_CONTROLLER_H__ - -#include -#include - -#include "base/win/scoped_handle.h" -#include "sandbox/src/sandbox.h" - -namespace sandbox { - -// See winerror.h for details. -#define SEVERITY_INFO_FLAGS 0x40000000 -#define SEVERITY_ERROR_FLAGS 0xC0000000 -#define CUSTOMER_CODE 0x20000000 -#define SBOX_TESTS_FACILITY 0x05B10000 - -// All the possible error codes returned by the child process in -// the sandbox. -enum SboxTestResult { - SBOX_TEST_FIRST_RESULT = CUSTOMER_CODE | SBOX_TESTS_FACILITY, - SBOX_TEST_SUCCEEDED, - SBOX_TEST_PING_OK, - SBOX_TEST_FIRST_INFO = SBOX_TEST_FIRST_RESULT | SEVERITY_INFO_FLAGS, - SBOX_TEST_DENIED, // Access was denied. - SBOX_TEST_NOT_FOUND, // The resource was not found. - SBOX_TEST_FIRST_ERROR = SBOX_TEST_FIRST_RESULT | SEVERITY_ERROR_FLAGS, - SBOX_TEST_INVALID_PARAMETER, - SBOX_TEST_FAILED_TO_RUN_TEST, - SBOX_TEST_FAILED_TO_EXECUTE_COMMAND, - SBOX_TEST_TIMED_OUT, - SBOX_TEST_FAILED, - SBOX_TEST_LAST_RESULT -}; - -inline bool IsSboxTestsResult(SboxTestResult result) { - unsigned int code = static_cast(result); - unsigned int first = static_cast(SBOX_TEST_FIRST_RESULT); - unsigned int last = static_cast(SBOX_TEST_LAST_RESULT); - return (code > first) && (code < last); -} - -enum SboxTestsState { - MIN_STATE = 1, - BEFORE_INIT, - BEFORE_REVERT, - AFTER_REVERT, - EVERY_STATE, - MAX_STATE -}; - -#define SBOX_TESTS_API __declspec(dllexport) -#define SBOX_TESTS_COMMAND extern "C" SBOX_TESTS_API - -extern "C" { -typedef int (*CommandFunction)(int argc, wchar_t **argv); -} - -// Class to facilitate the launch of a test inside the sandbox. -class TestRunner { - public: - TestRunner(JobLevel job_level, TokenLevel startup_token, - TokenLevel main_token); - - TestRunner(); - - ~TestRunner(); - - // Adds a rule to the policy. The parameters are the same as the AddRule - // function in the sandbox. - bool AddRule(TargetPolicy::SubSystem subsystem, - TargetPolicy::Semantics semantics, - const wchar_t* pattern); - - // Adds a filesystem rules with the path of a file in system32. The function - // appends "pattern" to "system32" and then call AddRule. Return true if the - // function succeeds. - bool AddRuleSys32(TargetPolicy::Semantics semantics, const wchar_t* pattern); - - // Adds a filesystem rules to the policy. Returns true if the functions - // succeeds. - bool AddFsRule(TargetPolicy::Semantics semantics, const wchar_t* pattern); - - // Starts a child process in the sandbox and ask it to run |command|. Returns - // a SboxTestResult. By default, the test runs AFTER_REVERT. - int RunTest(const wchar_t* command); - - // Sets the timeout value for the child to run the command and return. - void SetTimeout(DWORD timeout_ms); - - // Sets TestRunner to return without waiting for the process to exit. - void SetAsynchronous(bool is_async) { is_async_ = is_async; } - - // Sets TestRunner to return without waiting for the process to exit. - void SetUnsandboxed(bool is_no_sandbox) { no_sandbox_ = is_no_sandbox; } - - // Sets the desired state for the test to run. - void SetTestState(SboxTestsState desired_state); - - // Returns the pointers to the policy object. It can be used to modify - // the policy manually. - TargetPolicy* GetPolicy(); - - // Return the process handle for an asynchronous test. - HANDLE process() { return target_process_; } - - // Return the process ID for an asynchronous test. - DWORD process_id() { return target_process_id_; } - - private: - // Initializes the data in the object. Sets is_init_ to tree if the - // function succeeds. This is meant to be called from the constructor. - void Init(JobLevel job_level, TokenLevel startup_token, - TokenLevel main_token); - - // The actual runner. - int InternalRunTest(const wchar_t* command); - - BrokerServices* broker_; - TargetPolicy* policy_; - DWORD timeout_; - SboxTestsState state_; - bool is_init_; - bool is_async_; - bool no_sandbox_; - base::win::ScopedHandle target_process_; - DWORD target_process_id_; -}; - -// Returns the broker services. -BrokerServices* GetBroker(); - -// Constructs a full path to a file inside the system32 (or syswow64) folder. -std::wstring MakePathToSys(const wchar_t* name, bool is_obj_man_path); - -// Runs the given test on the target process. -int DispatchCall(int argc, wchar_t **argv); - -} // namespace sandbox - -#endif // SANDBOX_TESTS_COMMON_CONTROLLER_H_ diff --git a/sandbox/tests/common/test_utils.cc b/sandbox/tests/common/test_utils.cc deleted file mode 100644 index 929c322..0000000 --- a/sandbox/tests/common/test_utils.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/tests/common/test_utils.h" - -#include - -typedef struct _REPARSE_DATA_BUFFER { - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - union { - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - }; -} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; - -// Sets a reparse point. |source| will now point to |target|. Returns true if -// the call succeeds, false otherwise. -bool SetReparsePoint(HANDLE source, const wchar_t* target) { - USHORT size_target = static_cast(wcslen(target)) * sizeof(target[0]); - - char buffer[2000] = {0}; - DWORD returned; - - REPARSE_DATA_BUFFER* data = reinterpret_cast(buffer); - - data->ReparseTag = 0xa0000003; - memcpy(data->MountPointReparseBuffer.PathBuffer, target, size_target + 2); - data->MountPointReparseBuffer.SubstituteNameLength = size_target; - data->MountPointReparseBuffer.PrintNameOffset = size_target + 2; - data->ReparseDataLength = size_target + 4 + 8; - - int data_size = data->ReparseDataLength + 8; - - if (!DeviceIoControl(source, FSCTL_SET_REPARSE_POINT, &buffer, data_size, - NULL, 0, &returned, NULL)) { - return false; - } - return true; -} - -// Delete the reparse point referenced by |source|. Returns true if the call -// succeeds, false otherwise. -bool DeleteReparsePoint(HANDLE source) { - DWORD returned; - REPARSE_DATA_BUFFER data = {0}; - data.ReparseTag = 0xa0000003; - if (!DeviceIoControl(source, FSCTL_DELETE_REPARSE_POINT, &data, 8, NULL, 0, - &returned, NULL)) { - return false; - } - - return true; -} diff --git a/sandbox/tests/common/test_utils.h b/sandbox/tests/common/test_utils.h deleted file mode 100644 index 9e17660..0000000 --- a/sandbox/tests/common/test_utils.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_TESTS_COMMON_TEST_UTILS_H_ -#define SANDBOX_TESTS_COMMON_TEST_UTILS_H_ - -#include - -// Sets a reparse point. |source| will now point to |target|. Returns true if -// the call succeeds, false otherwise. -bool SetReparsePoint(HANDLE source, const wchar_t* target); - -// Delete the reparse point referenced by |source|. Returns true if the call -// succeeds, false otherwise. -bool DeleteReparsePoint(HANDLE source); - -#endif // SANDBOX_TESTS_COMMON_TEST_UTILS_H_ - diff --git a/sandbox/tests/integration_tests/integration_tests.cc b/sandbox/tests/integration_tests/integration_tests.cc deleted file mode 100644 index 5096abb..0000000 --- a/sandbox/tests/integration_tests/integration_tests.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" -#include "sandbox/tests/common/controller.h" - -int wmain(int argc, wchar_t **argv) { - if (argc >= 2) { - if (0 == _wcsicmp(argv[1], L"-child") || - 0 == _wcsicmp(argv[1], L"-child-no-sandbox")) - // This instance is a child, not the test. - return sandbox::DispatchCall(argc, argv); - } - - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/sandbox/tests/integration_tests/integration_tests_test.cc b/sandbox/tests/integration_tests/integration_tests_test.cc deleted file mode 100644 index b610681..0000000 --- a/sandbox/tests/integration_tests/integration_tests_test.cc +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Some tests for the framework itself. - -#include "testing/gtest/include/gtest/gtest.h" -#include "sandbox/src/sandbox.h" -#include "sandbox/src/target_services.h" -#include "sandbox/src/sandbox_factory.h" -#include "sandbox/tests/common/controller.h" - -namespace sandbox { - -// Returns the current process state. -SBOX_TESTS_COMMAND int IntegrationTestsTest_state(int argc, wchar_t **argv) { - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) - return BEFORE_INIT; - - if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) - return BEFORE_REVERT; - - return AFTER_REVERT; -} - -// Returns the current process state, keeping track of it. -SBOX_TESTS_COMMAND int IntegrationTestsTest_state2(int argc, wchar_t **argv) { - static SboxTestsState state = MIN_STATE; - if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) { - if (MIN_STATE == state) - state = BEFORE_INIT; - return state; - } - - if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) { - if (BEFORE_INIT == state) - state = BEFORE_REVERT; - return state; - } - - if (BEFORE_REVERT == state) - state = AFTER_REVERT; - return state; -} - -// Returns the number of arguments -SBOX_TESTS_COMMAND int IntegrationTestsTest_args(int argc, wchar_t **argv) { - for (int i = 0; i < argc; i++) { - wchar_t argument[20]; - size_t argument_bytes = wcslen(argv[i]) * sizeof(wchar_t); - memcpy(argument, argv[i], __min(sizeof(argument), argument_bytes)); - } - - return argc; -} - -TEST(IntegrationTestsTest, CallsBeforeInit) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(BEFORE_INIT); - ASSERT_EQ(BEFORE_INIT, runner.RunTest(L"IntegrationTestsTest_state")); -} - -TEST(IntegrationTestsTest, CallsBeforeRevert) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(BEFORE_REVERT); - ASSERT_EQ(BEFORE_REVERT, runner.RunTest(L"IntegrationTestsTest_state")); -} - -TEST(IntegrationTestsTest, CallsAfterRevert) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(AFTER_REVERT); - ASSERT_EQ(AFTER_REVERT, runner.RunTest(L"IntegrationTestsTest_state")); -} - -TEST(IntegrationTestsTest, CallsEveryState) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(EVERY_STATE); - ASSERT_EQ(AFTER_REVERT, runner.RunTest(L"IntegrationTestsTest_state2")); -} - -TEST(IntegrationTestsTest, ForwardsArguments) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(BEFORE_INIT); - ASSERT_EQ(1, runner.RunTest(L"IntegrationTestsTest_args first")); - ASSERT_EQ(4, runner.RunTest(L"IntegrationTestsTest_args first second third " - L"fourth")); -} - -} // namespace sandbox diff --git a/sandbox/tests/integration_tests/sbox_integration_tests.vcproj b/sandbox/tests/integration_tests/sbox_integration_tests.vcproj deleted file mode 100644 index 53816e7..0000000 --- a/sandbox/tests/integration_tests/sbox_integration_tests.vcproj +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sandbox/tests/unit_tests/sbox_unittests.vcproj b/sandbox/tests/unit_tests/sbox_unittests.vcproj deleted file mode 100644 index a2df792..0000000 --- a/sandbox/tests/unit_tests/sbox_unittests.vcproj +++ /dev/null @@ -1,258 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sandbox/tests/unit_tests/unit_tests.cc b/sandbox/tests/unit_tests/unit_tests.cc deleted file mode 100644 index a9623db..0000000 --- a/sandbox/tests/unit_tests/unit_tests.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" - -int wmain(int argc, wchar_t **argv) { - if (argc >= 2) { - if (0 == _wcsicmp(argv[1], L"-child")) - // This instance is a child, not the test. - return 0; - } - - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/sandbox/tests/validation_tests/commands.cc b/sandbox/tests/validation_tests/commands.cc deleted file mode 100644 index d99451f..0000000 --- a/sandbox/tests/validation_tests/commands.cc +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "sandbox/tests/validation_tests/commands.h" - -#include "sandbox/tests/common/controller.h" - -namespace { - -// Returns the HKEY corresponding to name. If there is no HKEY corresponding -// to the name it returns NULL. -HKEY GetHKEYFromString(const std::wstring &name) { - if (L"HKLM" == name) - return HKEY_LOCAL_MACHINE; - else if (L"HKCR" == name) - return HKEY_CLASSES_ROOT; - else if (L"HKCC" == name) - return HKEY_CURRENT_CONFIG; - else if (L"HKCU" == name) - return HKEY_CURRENT_USER; - else if (L"HKU" == name) - return HKEY_USERS; - - return NULL; -} - -// Modifies string to remove the leading and trailing quotes. -void trim_quote(std::wstring* string) { - std::wstring::size_type pos1 = string->find_first_not_of(L'"'); - std::wstring::size_type pos2 = string->find_last_not_of(L'"'); - - if (std::wstring::npos == pos1 || std::wstring::npos == pos2) - (*string) = L""; - else - (*string) = string->substr(pos1, pos2 + 1); -} - -int TestOpenFile(std::wstring path, bool for_write) { - wchar_t path_expanded[MAX_PATH + 1] = {0}; - DWORD size = ::ExpandEnvironmentStrings(path.c_str(), path_expanded, - MAX_PATH); - if (!size) - return sandbox::SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - HANDLE file; - file = ::CreateFile(path_expanded, - for_write ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, // No security attributes. - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - NULL); // No template. - - if (INVALID_HANDLE_VALUE != file) { - ::CloseHandle(file); - return sandbox::SBOX_TEST_SUCCEEDED; - } else { - if (ERROR_ACCESS_DENIED == ::GetLastError()) { - return sandbox::SBOX_TEST_DENIED; - } else { - return sandbox::SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - } -} - -} // namespace - -namespace sandbox { - -SBOX_TESTS_COMMAND int ValidWindow(int argc, wchar_t **argv) { - if (1 != argc) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - HWND window = reinterpret_cast(static_cast(_wtoi(argv[0]))); - - return TestValidWindow(window); -} - -int TestValidWindow(HWND window) { - if (::IsWindow(window)) - return SBOX_TEST_SUCCEEDED; - - return SBOX_TEST_DENIED; -} - -SBOX_TESTS_COMMAND int OpenProcessCmd(int argc, wchar_t **argv) { - if (2 != argc) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - DWORD process_id = _wtol(argv[0]); - DWORD access_mask = _wtol(argv[1]); - return TestOpenProcess(process_id, access_mask); -} - -int TestOpenProcess(DWORD process_id, DWORD access_mask) { - HANDLE process = ::OpenProcess(access_mask, - FALSE, // Do not inherit handle. - process_id); - if (NULL == process) { - if (ERROR_ACCESS_DENIED == ::GetLastError()) { - return SBOX_TEST_DENIED; - } else { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - } else { - ::CloseHandle(process); - return SBOX_TEST_SUCCEEDED; - } -} - -SBOX_TESTS_COMMAND int OpenThreadCmd(int argc, wchar_t **argv) { - if (1 != argc) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - DWORD thread_id = _wtoi(argv[0]); - return TestOpenThread(thread_id); -} - -int TestOpenThread(DWORD thread_id) { - - HANDLE thread = ::OpenThread(THREAD_QUERY_INFORMATION, - FALSE, // Do not inherit handles. - thread_id); - - if (NULL == thread) { - if (ERROR_ACCESS_DENIED == ::GetLastError()) { - return SBOX_TEST_DENIED; - } else { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } - } else { - ::CloseHandle(thread); - return SBOX_TEST_SUCCEEDED; - } -} - -SBOX_TESTS_COMMAND int OpenFile(int argc, wchar_t **argv) { - if (1 != argc) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - std::wstring path = argv[0]; - trim_quote(&path); - - return TestOpenReadFile(path); -} - -int TestOpenReadFile(const std::wstring& path) { - return TestOpenFile(path, false); -} - -int TestOpenWriteFile(int argc, wchar_t **argv) { - if (1 != argc) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - std::wstring path = argv[0]; - trim_quote(&path); - - return TestOpenWriteFile(path); - } - -int TestOpenWriteFile(const std::wstring& path) { - return TestOpenFile(path, true); -} - -SBOX_TESTS_COMMAND int OpenKey(int argc, wchar_t **argv) { - if (0 == argc || argc > 2) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - // Get the hive. - HKEY base_key = GetHKEYFromString(argv[0]); - - // Get the subkey. - std::wstring subkey; - if (2 == argc) { - subkey = argv[1]; - trim_quote(&subkey); - } - - return TestOpenKey(base_key, subkey); -} - -int TestOpenKey(HKEY base_key, std::wstring subkey) { - HKEY key; - LONG err_code = ::RegOpenKeyEx(base_key, - subkey.c_str(), - 0, // Reserved, must be 0. - MAXIMUM_ALLOWED, - &key); - if (ERROR_SUCCESS == err_code) { - ::RegCloseKey(key); - return SBOX_TEST_SUCCEEDED; - } else if (ERROR_INVALID_HANDLE == err_code || - ERROR_ACCESS_DENIED == err_code) { - return SBOX_TEST_DENIED; - } else { - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - } -} - -// Returns true if the current's thread desktop is the interactive desktop. -// In Vista there is a more direct test but for XP and w2k we need to check -// the object name. -bool IsInteractiveDesktop(bool* is_interactive) { - HDESK current_desk = ::GetThreadDesktop(::GetCurrentThreadId()); - if (NULL == current_desk) { - return false; - } - wchar_t current_desk_name[256] = {0}; - if (!::GetUserObjectInformationW(current_desk, UOI_NAME, current_desk_name, - sizeof(current_desk_name), NULL)) { - return false; - } - *is_interactive = (0 == _wcsicmp(L"default", current_desk_name)); - return true; -} - -SBOX_TESTS_COMMAND int OpenInteractiveDesktop(int, wchar_t **) { - return TestOpenInputDesktop(); -} - -int TestOpenInputDesktop() { - bool is_interactive = false; - if (IsInteractiveDesktop(&is_interactive) && is_interactive) { - return SBOX_TEST_SUCCEEDED; - } - HDESK desk = ::OpenInputDesktop(0, FALSE, DESKTOP_CREATEWINDOW); - if (desk) { - ::CloseDesktop(desk); - return SBOX_TEST_SUCCEEDED; - } - return SBOX_TEST_DENIED; -} - -SBOX_TESTS_COMMAND int SwitchToSboxDesktop(int, wchar_t **) { - return TestSwitchDesktop(); -} - -int TestSwitchDesktop() { - HDESK sbox_desk = ::GetThreadDesktop(::GetCurrentThreadId()); - if (NULL == sbox_desk) { - return SBOX_TEST_FAILED; - } - if (::SwitchDesktop(sbox_desk)) { - return SBOX_TEST_SUCCEEDED; - } - return SBOX_TEST_DENIED; -} - -SBOX_TESTS_COMMAND int SleepCmd(int argc, wchar_t **argv) { - if (1 != argc) - return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - - ::Sleep(_wtoi(argv[0])); - return SBOX_TEST_SUCCEEDED; -} - - -} // namespace sandbox diff --git a/sandbox/tests/validation_tests/commands.h b/sandbox/tests/validation_tests/commands.h deleted file mode 100644 index 9b797a5..0000000 --- a/sandbox/tests/validation_tests/commands.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_TESTS_VALIDATION_TESTS_COMMANDS_H__ -#define SANDBOX_TESTS_VALIDATION_TESTS_COMMANDS_H__ - -namespace sandbox { - -// Checks if window is a real window. Returns a SboxTestResult. -int TestValidWindow(HWND window); - -// Tries to open the process_id. Returns a SboxTestResult. -int TestOpenProcess(DWORD process_id, DWORD access_mask); - -// Tries to open thread_id. Returns a SboxTestResult. -int TestOpenThread(DWORD thread_id); - -// Tries to open path for read access. Returns a SboxTestResult. -int TestOpenReadFile(const std::wstring& path); - -// Tries to open path for write access. Returns a SboxTestResult. -int TestOpenWriteFile(const std::wstring& path); - -// Tries to open a registry key. -int TestOpenKey(HKEY base_key, std::wstring subkey); - -// Tries to open the workstation's input desktop as long as the -// current desktop is not the interactive one. Returns a SboxTestResult. -int TestOpenInputDesktop(); - -// Tries to switch the interactive desktop. Returns a SboxTestResult. -int TestSwitchDesktop(); - -} // namespace sandbox - -#endif // SANDBOX_TESTS_VALIDATION_TESTS_COMMANDS_H__ diff --git a/sandbox/tests/validation_tests/sbox_validation_tests.vcproj b/sandbox/tests/validation_tests/sbox_validation_tests.vcproj deleted file mode 100644 index 9b7b599..0000000 --- a/sandbox/tests/validation_tests/sbox_validation_tests.vcproj +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sandbox/tests/validation_tests/suite.cc b/sandbox/tests/validation_tests/suite.cc deleted file mode 100644 index 3147f70..0000000 --- a/sandbox/tests/validation_tests/suite.cc +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains the validation tests for the sandbox. -// It includes the tests that need to be performed inside the -// sandbox. - -#include - -#include "base/win/windows_version.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "sandbox/tests/common/controller.h" - -#pragma comment(lib, "shlwapi.lib") - -namespace { - -void TestProcessAccess(sandbox::TestRunner* runner, DWORD target) { - const wchar_t *kCommandTemplate = L"OpenProcessCmd %d %d"; - wchar_t command[1024] = {0}; - - // Test all the scary process permissions. - wsprintf(command, kCommandTemplate, target, PROCESS_CREATE_THREAD); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); - wsprintf(command, kCommandTemplate, target, PROCESS_DUP_HANDLE); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); - wsprintf(command, kCommandTemplate, target, PROCESS_SET_INFORMATION); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); - wsprintf(command, kCommandTemplate, target, PROCESS_VM_OPERATION); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); - wsprintf(command, kCommandTemplate, target, PROCESS_VM_READ); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); - wsprintf(command, kCommandTemplate, target, PROCESS_VM_WRITE); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); - wsprintf(command, kCommandTemplate, target, PROCESS_QUERY_INFORMATION); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); - wsprintf(command, kCommandTemplate, target, WRITE_DAC); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); - wsprintf(command, kCommandTemplate, target, WRITE_OWNER); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); - wsprintf(command, kCommandTemplate, target, READ_CONTROL); - EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); -} - -} // namespace - -namespace sandbox { - -// Returns true if the volume that contains any_path supports ACL security. The -// input path can contain unexpanded environment strings. Returns false on any -// failure or if the file system does not support file security (such as FAT). -bool VolumeSupportsACLs(const wchar_t* any_path) { - wchar_t expand[MAX_PATH +1]; - DWORD len =::ExpandEnvironmentStringsW(any_path, expand, _countof(expand)); - if (0 == len) return false; - if (len > _countof(expand)) return false; - if (!::PathStripToRootW(expand)) return false; - DWORD fs_flags = 0; - if (!::GetVolumeInformationW(expand, NULL, 0, 0, NULL, &fs_flags, NULL, 0)) - return false; - if (fs_flags & FILE_PERSISTENT_ACLS) return true; - return false; -} - -// Tests if the suite is working properly. -TEST(ValidationSuite, TestSuite) { - TestRunner runner; - ASSERT_EQ(SBOX_TEST_PING_OK, runner.RunTest(L"ping")); -} - -// Tests if the file system is correctly protected by the sandbox. -TEST(ValidationSuite, TestFileSystem) { - // Do not perform the test if the system is using FAT or any other - // file system that does not have file security. - ASSERT_TRUE(VolumeSupportsACLs(L"%SystemDrive%\\")); - ASSERT_TRUE(VolumeSupportsACLs(L"%SystemRoot%\\")); - ASSERT_TRUE(VolumeSupportsACLs(L"%ProgramFiles%\\")); - ASSERT_TRUE(VolumeSupportsACLs(L"%Temp%\\")); - ASSERT_TRUE(VolumeSupportsACLs(L"%AppData%\\")); - - TestRunner runner; - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %SystemDrive%")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %SystemRoot%")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %ProgramFiles%")); - EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"OpenFile %SystemRoot%\\System32")); - EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"OpenFile %SystemRoot%\\explorer.exe")); - EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"OpenFile %SystemRoot%\\Cursors\\arrow_i.cur")); - EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"OpenFile %AllUsersProfile%")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %Temp%")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %AppData%")); -} - -// Tests if the registry is correctly protected by the sandbox. -TEST(ValidationSuite, TestRegistry) { - TestRunner runner; - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenKey HKLM")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenKey HKCU")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenKey HKU")); - EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest( - L"OpenKey HKLM " - L"\"Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon\"")); -} - -// Tests that the permissions on the Windowstation does not allow the sandbox -// to get to the interactive desktop or to make the sbox desktop interactive. -TEST(ValidationSuite, TestDesktop) { - TestRunner runner; - runner.GetPolicy()->SetAlternateDesktop(false); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenInteractiveDesktop NULL")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"SwitchToSboxDesktop NULL")); -} - -// Tests if the windows are correctly protected by the sandbox. -TEST(ValidationSuite, TestWindows) { - TestRunner runner; - wchar_t command[1024] = {0}; - - wsprintf(command, L"ValidWindow %d", ::GetDesktopWindow()); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); - - wsprintf(command, L"ValidWindow %d", ::FindWindow(NULL, NULL)); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); -} - -// Tests that a locked-down process cannot open another locked-down process. -TEST(ValidationSuite, TestProcessDenyLockdown) { - TestRunner runner; - TestRunner target; - wchar_t command[1024] = {0}; - - target.SetAsynchronous(true); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"SleepCmd 30000")); - - TestProcessAccess(&runner, target.process_id()); -} - -// Tests that a low-integrity process cannot open a locked-down process (due -// to the integrity label changing after startup via SetDelayedIntegrityLevel). -TEST(ValidationSuite, TestProcessDenyLowIntegrity) { - // This test applies only to Vista and above. - if (base::win::Version() < base::win::VERSION_VISTA) - return; - - TestRunner runner; - TestRunner target; - wchar_t command[1024] = {0}; - - target.SetAsynchronous(true); - target.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW); - - runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW); - runner.GetPolicy()->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS, - USER_INTERACTIVE); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"SleepCmd 30000")); - - TestProcessAccess(&runner, target.process_id()); -} - -// Tests that a locked-down process cannot open a low-integrity process. -TEST(ValidationSuite, TestProcessDenyBelowLowIntegrity) { - // This test applies only to Vista and above. - if (base::win::Version() < base::win::VERSION_VISTA) - return; - - TestRunner runner; - TestRunner target; - wchar_t command[1024] = {0}; - - target.SetAsynchronous(true); - target.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW); - target.GetPolicy()->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS, - USER_INTERACTIVE); - - runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_UNTRUSTED); - runner.GetPolicy()->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS, - USER_INTERACTIVE); - - EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"SleepCmd 30000")); - - TestProcessAccess(&runner, target.process_id()); -} - -// Tests if the threads are correctly protected by the sandbox. -TEST(ValidationSuite, TestThread) { - TestRunner runner; - wchar_t command[1024] = {0}; - - wsprintf(command, L"OpenThreadCmd %d", ::GetCurrentThreadId()); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); -} - -} // namespace sandbox diff --git a/sandbox/tests/validation_tests/unit_tests.cc b/sandbox/tests/validation_tests/unit_tests.cc deleted file mode 100644 index 490f7ec..0000000 --- a/sandbox/tests/validation_tests/unit_tests.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" -#include "sandbox/tests/common/controller.h" - -int wmain(int argc, wchar_t **argv) { - if (argc >= 2) { - if (0 == _wcsicmp(argv[1], L"-child")) - return sandbox::DispatchCall(argc, argv); - } - - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/sandbox/tools/finder/finder.cc b/sandbox/tools/finder/finder.cc deleted file mode 100644 index 26fc1b9..0000000 --- a/sandbox/tools/finder/finder.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/restricted_token.h" -#include "sandbox/src/restricted_token_utils.h" -#include "sandbox/tools/finder/finder.h" - -Finder::Finder() { - file_output_ = NULL; - object_type_ = 0; - access_type_ = 0; - token_handle_ = NULL; - memset(filesystem_stats_, 0, sizeof(filesystem_stats_)); - memset(registry_stats_, 0, sizeof(registry_stats_)); - memset(kernel_object_stats_, 0, sizeof(kernel_object_stats_)); -} - -Finder::~Finder() { - if (token_handle_) - ::CloseHandle(token_handle_); -} - -DWORD Finder::Init(sandbox::TokenLevel token_type, - DWORD object_type, - DWORD access_type, - FILE *file_output) { - DWORD err_code = ERROR_SUCCESS; - - err_code = InitNT(); - if (ERROR_SUCCESS != err_code) - return err_code; - - object_type_ = object_type; - access_type_ = access_type; - file_output_ = file_output; - - err_code = sandbox::CreateRestrictedToken(&token_handle_, token_type, - sandbox::INTEGRITY_LEVEL_LAST, - sandbox::PRIMARY); - return err_code; -} - -DWORD Finder::Scan() { - if (!token_handle_) { - return ERROR_NO_TOKEN; - } - - if (object_type_ & kScanRegistry) { - ParseRegistry(HKEY_LOCAL_MACHINE, L"HKLM\\"); - ParseRegistry(HKEY_USERS, L"HKU\\"); - ParseRegistry(HKEY_CURRENT_CONFIG, L"HKCC\\"); - } - - if (object_type_ & kScanFileSystem) { - ParseFileSystem(L"\\\\?\\C:"); - } - - if (object_type_ & kScanKernelObjects) { - ParseKernelObjects(L"\\"); - } - - return ERROR_SUCCESS; -} diff --git a/sandbox/tools/finder/finder.h b/sandbox/tools/finder/finder.h deleted file mode 100644 index 0686b1d..0000000 --- a/sandbox/tools/finder/finder.h +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_TOOLS_FINDER_FINDER_H__ -#define SANDBOX_TOOLS_FINDER_FINDER_H__ - -#include "sandbox/src/restricted_token_utils.h" -#include "sandbox/tools/finder/ntundoc.h" - -// Type of stats that we calculate during the Scan operation -enum Stats { - READ = 0, // Number of objects with read access - WRITE, // Number of objects with write access - ALL, // Number of objects with r/w access - PARSE, // Number of objects parsed - BROKEN, // Number of errors while parsing the objects - SIZE_STATS // size of the enum -}; - -const int kScanRegistry = 0x01; -const int kScanFileSystem = 0x02; -const int kScanKernelObjects = 0x04; - -const int kTestForRead = 0x01; -const int kTestForWrite = 0x02; -const int kTestForAll = 0x04; - -#define FS_ERR L"FILE-ERROR" -#define OBJ_ERR L"OBJ-ERROR" -#define REG_ERR L"REG_ERROR" -#define OBJ L"OBJ" -#define FS L"FILE" -#define REG L"REG" - -// The impersonater class will impersonate a token when the object is created -// and revert when the object is going out of scope. -class Impersonater { - public: - Impersonater(HANDLE token_handle) { - if (token_handle) - ::ImpersonateLoggedOnUser(token_handle); - }; - ~Impersonater() { - ::RevertToSelf(); - }; -}; - -// The finder class handles the search of objects (file system, registry, kernel -// objects) on the system that can be opened by a restricted token. It can -// support multiple levels of restriction for the restricted token and can check -// for read, write or r/w access. It outputs the results to a file or stdout. -class Finder { - public: - Finder(); - ~Finder(); - DWORD Init(sandbox::TokenLevel token_type, DWORD object_type, - DWORD access_type, FILE *file_output); - DWORD Scan(); - - private: - // Parses a file system path and perform an access check on all files and - // folder found. - // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the - // win32 error code associated with the error. - DWORD ParseFileSystem(ATL::CString path); - - // Parses a registry hive referenced by "key" and performs an access check on - // all subkeys found. - // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the - // win32 error code associated with the error. - DWORD ParseRegistry(HKEY key, ATL::CString print_name); - - // Parses the kernel namespace beginning at "path" and performs an access - // check on all objects found. However, only some object types are supported, - // all non supported objects are ignored. - // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the - // win32 error code associated with the error. - DWORD ParseKernelObjects(ATL::CString path); - - // Checks if "path" can be accessed with the restricted token. - // Returns the access granted. - DWORD TestFileAccess(ATL::CString path); - - // Checks if the registry key with the path key\name can be accessed with the - // restricted token. - // print_name is only use for logging purpose. - // Returns the access granted. - DWORD TestRegAccess(HKEY key, ATL::CString name, ATL::CString print_name); - - // Checks if the kernel object "path" of type "type" can be accessed with - // the restricted token. - // Returns the access granted. - DWORD TestKernelObjectAccess(ATL::CString path, ATL::CString type); - - // Outputs information to the logfile - void Output(ATL::CString type, ATL::CString access, ATL::CString info) { - fprintf(file_output_, "\n%S;%S;%S", type.GetBuffer(), access.GetBuffer(), - info.GetBuffer()); - }; - - // Output information to the log file. - void Output(ATL::CString type, DWORD error, ATL::CString info) { - fprintf(file_output_, "\n%S;0x%X;%S", type.GetBuffer(), error, - info.GetBuffer()); - }; - - // Set func_to_call to the function pointer of the function used to handle - // requests for the kernel objects of type "type". If the type is not - // supported at the moment the function returns false and the func_to_call - // parameter is not modified. - bool GetFunctionForType(ATL::CString type, NTGENERICOPEN * func_to_call); - - // Initializes the NT function pointers to be able to use all the needed - // functions in NTDDL. - // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the - // win32 error code associated with the error. - DWORD InitNT(); - - // Calls func_to_call with the parameters desired_access, object_attributes - // and handle. func_to_call is a pointer to a function to open a kernel - // object. - NTSTATUS NtGenericOpen(ACCESS_MASK desired_access, - OBJECT_ATTRIBUTES *object_attributes, - NTGENERICOPEN func_to_call, - HANDLE *handle); - - // Type of object to check for. - DWORD object_type_; - // Access to try. - DWORD access_type_; - // Output file for the results. - FILE * file_output_; - // Handle to the restricted token. - HANDLE token_handle_; - // Stats containing the number of operations performed on the different - // objects. - int filesystem_stats_[SIZE_STATS]; - int registry_stats_[SIZE_STATS]; - int kernel_object_stats_[SIZE_STATS]; -}; - -#endif // SANDBOX_TOOLS_FINDER_FINDER_H__ diff --git a/sandbox/tools/finder/finder.vcproj b/sandbox/tools/finder/finder.vcproj deleted file mode 100644 index 787c847..0000000 --- a/sandbox/tools/finder/finder.vcproj +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sandbox/tools/finder/finder_fs.cc b/sandbox/tools/finder/finder_fs.cc deleted file mode 100644 index 7375b86..0000000 --- a/sandbox/tools/finder/finder_fs.cc +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/restricted_token.h" -#include "sandbox/src/restricted_token_utils.h" -#include "sandbox/tools/finder/finder.h" - -DWORD Finder::ParseFileSystem(ATL::CString directory) { - WIN32_FIND_DATA find_data; - HANDLE find; - - //Search for items in the directory. - ATL::CString name_to_search = directory + L"\\*"; - find = ::FindFirstFile(name_to_search, &find_data); - if (INVALID_HANDLE_VALUE == find) { - DWORD error = ::GetLastError(); - Output(FS_ERR, error, directory); - filesystem_stats_[BROKEN]++; - return error; - } - - // parse all files or folders. - do { - if (_tcscmp(find_data.cFileName, L".") == 0 || - _tcscmp(find_data.cFileName, L"..") == 0) - continue; - - ATL::CString complete_name = directory + L"\\" + find_data.cFileName; - TestFileAccess(complete_name); - - // Call recursively the function if the path found is a directory. - if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { - ParseFileSystem(complete_name); - } - } while (::FindNextFile(find, &find_data) != 0); - - DWORD err_code = ::GetLastError(); - ::FindClose(find); - - if (ERROR_NO_MORE_FILES != err_code) { - Output(FS_ERR, err_code, directory); - filesystem_stats_[BROKEN]++; - return err_code; - } - - return ERROR_SUCCESS; -} - -DWORD Finder::TestFileAccess(ATL::CString name) { - Impersonater impersonate(token_handle_); - - filesystem_stats_[PARSE]++; - - HANDLE file; - if (access_type_ & kTestForAll) { - file = ::CreateFile(name.GetBuffer(), - GENERIC_ALL, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (file != INVALID_HANDLE_VALUE) { - filesystem_stats_[ALL]++; - Output(FS, L"R/W", name.GetBuffer()); - ::CloseHandle(file); - return GENERIC_ALL; - } else if (::GetLastError() != ERROR_ACCESS_DENIED) { - Output(FS_ERR, GetLastError(), name); - filesystem_stats_[BROKEN]++; - } - } - - if (access_type_ & kTestForWrite) { - file = ::CreateFile(name.GetBuffer(), - GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (file != INVALID_HANDLE_VALUE) { - filesystem_stats_[WRITE]++; - Output(FS, L"W", name); - ::CloseHandle(file); - return GENERIC_WRITE; - } else if (::GetLastError() != ERROR_ACCESS_DENIED) { - Output(FS_ERR, ::GetLastError(), name); - filesystem_stats_[BROKEN]++; - } - } - - if (access_type_ & kTestForRead) { - file = ::CreateFile(name.GetBuffer(), - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (file != INVALID_HANDLE_VALUE) { - filesystem_stats_[READ]++; - Output(FS, L"R", name); - ::CloseHandle(file); - return GENERIC_READ; - } else if (::GetLastError() != ERROR_ACCESS_DENIED) { - Output(FS_ERR, GetLastError(), name); - filesystem_stats_[BROKEN]++; - } - } - - return 0; -} diff --git a/sandbox/tools/finder/finder_kernel.cc b/sandbox/tools/finder/finder_kernel.cc deleted file mode 100644 index 66ab454..0000000 --- a/sandbox/tools/finder/finder_kernel.cc +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/restricted_token.h" -#include "sandbox/src/restricted_token_utils.h" -#include "sandbox/tools/finder/finder.h" -#include "sandbox/tools/finder/ntundoc.h" - -#define BUFFER_SIZE 0x800 -#define CHECKPTR(x) if (!x) return ::GetLastError() - -// NT API -NTQUERYDIRECTORYOBJECT NtQueryDirectoryObject; -NTOPENDIRECTORYOBJECT NtOpenDirectoryObject; -NTOPENEVENT NtOpenEvent; -NTOPENJOBOBJECT NtOpenJobObject; -NTOPENKEYEDEVENT NtOpenKeyedEvent; -NTOPENMUTANT NtOpenMutant; -NTOPENSECTION NtOpenSection; -NTOPENSEMAPHORE NtOpenSemaphore; -NTOPENSYMBOLICLINKOBJECT NtOpenSymbolicLinkObject; -NTOPENTIMER NtOpenTimer; -NTOPENFILE NtOpenFile; -NTCLOSE NtClose; - -DWORD Finder::InitNT() { - HMODULE ntdll_handle = ::LoadLibrary(L"ntdll.dll"); - CHECKPTR(ntdll_handle); - - NtOpenSymbolicLinkObject = (NTOPENSYMBOLICLINKOBJECT) ::GetProcAddress( - ntdll_handle, "NtOpenSymbolicLinkObject"); - CHECKPTR(NtOpenSymbolicLinkObject); - - NtQueryDirectoryObject = (NTQUERYDIRECTORYOBJECT) ::GetProcAddress( - ntdll_handle, "NtQueryDirectoryObject"); - CHECKPTR(NtQueryDirectoryObject); - - NtOpenDirectoryObject = (NTOPENDIRECTORYOBJECT) ::GetProcAddress( - ntdll_handle, "NtOpenDirectoryObject"); - CHECKPTR(NtOpenDirectoryObject); - - NtOpenKeyedEvent = (NTOPENKEYEDEVENT) ::GetProcAddress( - ntdll_handle, "NtOpenKeyedEvent"); - CHECKPTR(NtOpenKeyedEvent); - - NtOpenJobObject = (NTOPENJOBOBJECT) ::GetProcAddress( - ntdll_handle, "NtOpenJobObject"); - CHECKPTR(NtOpenJobObject); - - NtOpenSemaphore = (NTOPENSEMAPHORE) ::GetProcAddress( - ntdll_handle, "NtOpenSemaphore"); - CHECKPTR(NtOpenSemaphore); - - NtOpenSection = (NTOPENSECTION) ::GetProcAddress( - ntdll_handle, "NtOpenSection"); - CHECKPTR(NtOpenSection); - - NtOpenMutant= (NTOPENMUTANT) ::GetProcAddress(ntdll_handle, "NtOpenMutant"); - CHECKPTR(NtOpenMutant); - - NtOpenEvent = (NTOPENEVENT) ::GetProcAddress(ntdll_handle, "NtOpenEvent"); - CHECKPTR(NtOpenEvent); - - NtOpenTimer = (NTOPENTIMER) ::GetProcAddress(ntdll_handle, "NtOpenTimer"); - CHECKPTR(NtOpenTimer); - - NtOpenFile = (NTOPENFILE) ::GetProcAddress(ntdll_handle, "NtOpenFile"); - CHECKPTR(NtOpenFile); - - NtClose = (NTCLOSE) ::GetProcAddress(ntdll_handle, "NtClose"); - CHECKPTR(NtClose); - - return ERROR_SUCCESS; -} - -DWORD Finder::ParseKernelObjects(ATL::CString path) { - UNICODE_STRING unicode_str; - unicode_str.Length = (USHORT)path.GetLength()*2; - unicode_str.MaximumLength = (USHORT)path.GetLength()*2+2; - unicode_str.Buffer = path.GetBuffer(); - - OBJECT_ATTRIBUTES path_attributes; - InitializeObjectAttributes(&path_attributes, - &unicode_str, - 0, // No Attributes - NULL, // No Root Directory - NULL); // No Security Descriptor - - - DWORD object_index = 0; - DWORD data_written = 0; - - // TODO(nsylvain): Do not use BUFFER_SIZE. Try to get the size - // dynamically. - OBJDIR_INFORMATION *object_directory_info = - (OBJDIR_INFORMATION*) ::HeapAlloc(GetProcessHeap(), - 0, - BUFFER_SIZE); - - HANDLE file_handle; - NTSTATUS status_code = NtOpenDirectoryObject(&file_handle, - DIRECTORY_QUERY, - &path_attributes); - if (status_code != 0) - return ERROR_UNIDENTIFIED_ERROR; - - status_code = NtQueryDirectoryObject(file_handle, - object_directory_info, - BUFFER_SIZE, - TRUE, // Get Next Index - TRUE, // Ignore Input Index - &object_index, - &data_written); - - if (status_code != 0) - return ERROR_UNIDENTIFIED_ERROR; - - while (NtQueryDirectoryObject(file_handle, object_directory_info, - BUFFER_SIZE, TRUE, FALSE, &object_index, - &data_written) == 0 ) { - ATL::CString cur_path(object_directory_info->ObjectName.Buffer, - object_directory_info->ObjectName.Length / sizeof(WCHAR)); - - ATL::CString cur_type(object_directory_info->ObjectTypeName.Buffer, - object_directory_info->ObjectTypeName.Length / sizeof(WCHAR)); - - ATL::CString new_path; - if (path == L"\\") { - new_path = path + cur_path; - } else { - new_path = path + L"\\" + cur_path; - } - - TestKernelObjectAccess(new_path, cur_type); - - // Call the function recursively for all subdirectories - if (cur_type == L"Directory") { - ParseKernelObjects(new_path); - } - } - - NtClose(file_handle); - return ERROR_SUCCESS; -} - -DWORD Finder::TestKernelObjectAccess(ATL::CString path, ATL::CString type) { - Impersonater impersonate(token_handle_); - - kernel_object_stats_[PARSE]++; - - NTGENERICOPEN func = NULL; - GetFunctionForType(type, &func); - - if (!func) { - kernel_object_stats_[BROKEN]++; - Output(OBJ_ERR, type + L" Unsupported", path); - return ERROR_UNSUPPORTED_TYPE; - } - - UNICODE_STRING unicode_str; - unicode_str.Length = (USHORT)path.GetLength()*2; - unicode_str.MaximumLength = (USHORT)path.GetLength()*2+2; - unicode_str.Buffer = path.GetBuffer(); - - OBJECT_ATTRIBUTES path_attributes; - InitializeObjectAttributes(&path_attributes, - &unicode_str, - 0, // No Attributes - NULL, // No Root Directory - NULL); // No Security Descriptor - - HANDLE handle; - NTSTATUS status_code = 0; - - if (access_type_ & kTestForAll) { - status_code = NtGenericOpen(GENERIC_ALL, &path_attributes, func, &handle); - if (STATUS_SUCCESS == status_code) { - kernel_object_stats_[ALL]++; - Output(OBJ, L"R/W", path); - NtClose(handle); - return GENERIC_ALL; - } else if (status_code != EXCEPTION_ACCESS_VIOLATION && - status_code != STATUS_ACCESS_DENIED) { - Output(OBJ_ERR, status_code, path); - kernel_object_stats_[BROKEN]++; - } - } - - if (access_type_ & kTestForWrite) { - status_code = NtGenericOpen(GENERIC_WRITE, &path_attributes, func, &handle); - if (STATUS_SUCCESS == status_code) { - kernel_object_stats_[WRITE]++; - Output(OBJ, L"W", path); - NtClose(handle); - return GENERIC_WRITE; - } else if (status_code != EXCEPTION_ACCESS_VIOLATION && - status_code != STATUS_ACCESS_DENIED) { - Output(OBJ_ERR, status_code, path); - kernel_object_stats_[BROKEN]++; - } - } - - if (access_type_ & kTestForRead) { - status_code = NtGenericOpen(GENERIC_READ, &path_attributes, func, &handle); - if (STATUS_SUCCESS == status_code) { - kernel_object_stats_[READ]++; - Output(OBJ, L"R", path); - NtClose(handle); - return GENERIC_READ; - } else if (status_code != EXCEPTION_ACCESS_VIOLATION && - status_code != STATUS_ACCESS_DENIED) { - Output(OBJ_ERR, status_code, path); - kernel_object_stats_[BROKEN]++; - } - } - - return 0; -} - -NTSTATUS Finder::NtGenericOpen(ACCESS_MASK desired_access, - OBJECT_ATTRIBUTES *object_attributes, - NTGENERICOPEN func_to_call, - HANDLE *handle) { - return func_to_call(handle, desired_access, object_attributes); -} - -bool Finder::GetFunctionForType(ATL::CString type, - NTGENERICOPEN * func_to_call) { - NTGENERICOPEN func = NULL; - - if (type == L"Event") func = NtOpenEvent; - else if (type == L"Job") func = NtOpenJobObject; - else if (type == L"KeyedEvent") func = NtOpenKeyedEvent; - else if (type == L"Mutant") func = NtOpenMutant; - else if (type == L"Section") func = NtOpenSection; - else if (type == L"Semaphore") func = NtOpenSemaphore; - else if (type == L"Timer") func = NtOpenTimer; - else if (type == L"SymbolicLink") func = NtOpenSymbolicLinkObject; - else if (type == L"Directory") func = NtOpenDirectoryObject; - - if (func) { - *func_to_call = func; - return true; - } - - return false; -} diff --git a/sandbox/tools/finder/finder_registry.cc b/sandbox/tools/finder/finder_registry.cc deleted file mode 100644 index 4b18625..0000000 --- a/sandbox/tools/finder/finder_registry.cc +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/restricted_token.h" -#include "sandbox/src/restricted_token_utils.h" -#include "sandbox/tools/finder/finder.h" - -DWORD Finder::ParseRegistry(HKEY key, ATL::CString print_name) { - DWORD index = 0; - DWORD name_size = 2048; - wchar_t buffer[2048] = {0}; - // TODO(nsylvain): Don't hardcode 2048. Get the key len by calling the - // function. - LONG err_code = ::RegEnumKey(key, index, buffer, name_size); - while (ERROR_SUCCESS == err_code) { - ATL::CString name_complete = print_name + buffer + L"\\"; - TestRegAccess(key, buffer, name_complete); - - // Call the function recursively to parse all subkeys - HKEY key_to_parse; - err_code = ::RegOpenKeyEx(key, buffer, 0, KEY_ENUMERATE_SUB_KEYS, - &key_to_parse); - if (ERROR_SUCCESS == err_code) { - ParseRegistry(key_to_parse, name_complete); - ::RegCloseKey(key_to_parse); - } else { - registry_stats_[BROKEN]++; - Output(REG_ERR, err_code, name_complete); - } - - index++; - err_code = ::RegEnumKey(key, index, buffer, name_size); - } - - if (ERROR_NO_MORE_ITEMS != err_code) { - registry_stats_[BROKEN]++; - Output(REG_ERR, err_code, print_name); - } - - return ERROR_SUCCESS; -} - -DWORD Finder::TestRegAccess(HKEY key, ATL::CString name, - ATL::CString print_name) { - Impersonater impersonate(token_handle_); - - registry_stats_[PARSE]++; - - HKEY key_res; - LONG err_code = 0; - - if (access_type_ & kTestForAll) { - err_code = ::RegOpenKeyEx(key, name, 0, GENERIC_ALL, &key_res); - if (ERROR_SUCCESS == err_code) { - registry_stats_[ALL]++; - Output(REG, L"R/W", print_name); - ::RegCloseKey(key_res); - return GENERIC_ALL; - } else if (err_code != ERROR_ACCESS_DENIED) { - Output(REG_ERR, err_code, print_name); - registry_stats_[BROKEN]++; - } - } - - if (access_type_ & kTestForWrite) { - err_code = ::RegOpenKeyEx(key, name, 0, GENERIC_WRITE, &key_res); - if (ERROR_SUCCESS == err_code) { - registry_stats_[WRITE]++; - Output(REG, L"W", print_name); - ::RegCloseKey(key_res); - return GENERIC_WRITE; - } else if (err_code != ERROR_ACCESS_DENIED) { - Output(REG_ERR, err_code, print_name); - registry_stats_[BROKEN]++; - } - } - - if (access_type_ & kTestForRead) { - err_code = ::RegOpenKeyEx(key, name, 0, GENERIC_READ, &key_res); - if (ERROR_SUCCESS == err_code) { - registry_stats_[READ]++; - Output(REG, L"R", print_name); - ::RegCloseKey(key_res); - return GENERIC_READ; - } else if (err_code != ERROR_ACCESS_DENIED) { - Output(REG_ERR, err_code, print_name); - registry_stats_[BROKEN]++; - } - } - - return 0; -} diff --git a/sandbox/tools/finder/main.cc b/sandbox/tools/finder/main.cc deleted file mode 100644 index 6ffbec3..0000000 --- a/sandbox/tools/finder/main.cc +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/restricted_token_utils.h" -#include "sandbox/tools/finder/finder.h" - -#define PARAM_IS(y) (argc > i) && (_wcsicmp(argv[i], y) == 0) - -void PrintUsage(wchar_t *application_name) { - wprintf(L"\n\nUsage: \n %ls --token type --object ob1 [ob2 ob3] " - L"--access ac1 [ac2 ac3] [--log filename]", application_name); - wprintf(L"\n\n Token Types : \n\tLOCKDOWN \n\tRESTRICTED " - L"\n\tLIMITED_USER \n\tINTERACTIVE_USER \n\tNON_ADMIN \n\tUNPROTECTED"); - wprintf(L"\n Object Types: \n\tREG \n\tFILE \n\tKERNEL"); - wprintf(L"\n Access Types: \n\tR \n\tW \n\tALL"); - wprintf(L"\n\nSample: \n %ls --token LOCKDOWN --object REG FILE KERNEL " - L"--access R W ALL", application_name); -} - -int wmain(int argc, wchar_t* argv[]) { - // Extract the filename from the path. - wchar_t *app_name = wcsrchr(argv[0], L'\\'); - if (!app_name) { - app_name = argv[0]; - } else { - app_name++; - } - - // parameters to read - ATL::CString log_file; - sandbox::TokenLevel token_type = sandbox::USER_LOCKDOWN; - DWORD object_type = 0; - DWORD access_type = 0; - - // no arguments - if (argc == 1) { - PrintUsage(app_name); - return -1; - } - - // parse command line. - for (int i = 1; i < argc; ++i) { - if (PARAM_IS(L"--token")) { - i++; - if (argc > i) { - if (PARAM_IS(L"LOCKDOWN")) { - token_type = sandbox::USER_LOCKDOWN; - } else if (PARAM_IS(L"RESTRICTED")) { - token_type = sandbox::USER_RESTRICTED; - } else if (PARAM_IS(L"LIMITED_USER")) { - token_type = sandbox::USER_LIMITED; - } else if (PARAM_IS(L"INTERACTIVE_USER")) { - token_type = sandbox::USER_INTERACTIVE; - } else if (PARAM_IS(L"NON_ADMIN")) { - token_type = sandbox::USER_NON_ADMIN; - } else if (PARAM_IS(L"USER_RESTRICTED_SAME_ACCESS")) { - token_type = sandbox::USER_RESTRICTED_SAME_ACCESS; - } else if (PARAM_IS(L"UNPROTECTED")) { - token_type = sandbox::USER_UNPROTECTED; - } else { - wprintf(L"\nAbord. Invalid token type \"%ls\"", argv[i]); - PrintUsage(app_name); - return -1; - } - } - } else if (PARAM_IS(L"--object")) { - bool is_object = true; - do { - i++; - if (PARAM_IS(L"REG")) { - object_type |= kScanRegistry; - } else if (PARAM_IS(L"FILE")) { - object_type |= kScanFileSystem; - } else if (PARAM_IS(L"KERNEL")) { - object_type |= kScanKernelObjects; - } else { - is_object = false; - } - } while(is_object); - i--; - } else if (PARAM_IS(L"--access")) { - bool is_access = true; - do { - i++; - if (PARAM_IS(L"R")) { - access_type |= kTestForRead; - } else if (PARAM_IS(L"W")) { - access_type |= kTestForWrite; - } else if (PARAM_IS(L"ALL")) { - access_type |= kTestForAll; - } else { - is_access = false; - } - } while(is_access); - i--; - } else if (PARAM_IS(L"--log")) { - i++; - if (argc > i) { - log_file = argv[i]; - } - else { - wprintf(L"\nAbord. No log file specified"); - PrintUsage(app_name); - return -1; - } - } else { - wprintf(L"\nAbord. Unrecognized parameter \"%ls\"", argv[i]); - PrintUsage(app_name); - return -1; - } - } - - // validate parameters - if (0 == access_type) { - wprintf(L"\nAbord, Access type not specified"); - PrintUsage(app_name); - return -1; - } - - if (0 == object_type) { - wprintf(L"\nAbord, Object type not specified"); - PrintUsage(app_name); - return -1; - } - - - // Open log file - FILE * file_output; - if (log_file.GetLength()) { - errno_t err = _wfopen_s(&file_output, log_file, L"w"); - if (err) { - wprintf(L"\nAbord, Cannot open file \"%ls\"", log_file.GetBuffer()); - return -1; - } - } else { - file_output = stdout; - } - - Finder finder_obj; - finder_obj.Init(token_type, object_type, access_type, file_output); - finder_obj.Scan(); - - fclose(file_output); - - return 0; -} diff --git a/sandbox/tools/finder/ntundoc.h b/sandbox/tools/finder/ntundoc.h deleted file mode 100644 index dc8c3a5..0000000 --- a/sandbox/tools/finder/ntundoc.h +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_TOOLS_FINDER_NTUNDOC_H__ -#define SANDBOX_TOOLS_FINDER_NTUNDOC_H__ - -#define NTSTATUS ULONG -#define STATUS_SUCCESS 0x00000000 -#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004 -#define STATUS_ACCESS_DENIED 0xC0000022 -#define STATUS_BUFFER_OVERFLOW 0x80000005 - -typedef struct _LSA_UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING; - -typedef struct _OBJDIR_INFORMATION { - UNICODE_STRING ObjectName; - UNICODE_STRING ObjectTypeName; - BYTE Data[1]; -} OBJDIR_INFORMATION; - -typedef struct _OBJECT_ATTRIBUTES { - ULONG Length; - HANDLE RootDirectory; - UNICODE_STRING *ObjectName; - ULONG Attributes; - PVOID SecurityDescriptor; - PVOID SecurityQualityOfService; -} OBJECT_ATTRIBUTES; - -typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION { - ULONG Attributes; - ACCESS_MASK GrantedAccess; - ULONG HandleCount; - ULONG PointerCount; - ULONG Reserved[10]; // reserved for internal use - } PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION; - -typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION { - UNICODE_STRING TypeName; - ULONG Reserved [22]; // reserved for internal use -} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION; - -typedef enum _POOL_TYPE { - NonPagedPool, - PagedPool, - NonPagedPoolMustSucceed, - ReservedType, - NonPagedPoolCacheAligned, - PagedPoolCacheAligned, - NonPagedPoolCacheAlignedMustS -} POOL_TYPE; - -typedef struct _OBJECT_TYPE_INFORMATION { - UNICODE_STRING Name; - ULONG TotalNumberOfObjects; - ULONG TotalNumberOfHandles; - ULONG TotalPagedPoolUsage; - ULONG TotalNonPagedPoolUsage; - ULONG TotalNamePoolUsage; - ULONG TotalHandleTableUsage; - ULONG HighWaterNumberOfObjects; - ULONG HighWaterNumberOfHandles; - ULONG HighWaterPagedPoolUsage; - ULONG HighWaterNonPagedPoolUsage; - ULONG HighWaterNamePoolUsage; - ULONG HighWaterHandleTableUsage; - ULONG InvalidAttributes; - GENERIC_MAPPING GenericMapping; - ULONG ValidAccess; - BOOLEAN SecurityRequired; - BOOLEAN MaintainHandleCount; - USHORT MaintainTypeList; - POOL_TYPE PoolType; - ULONG PagedPoolUsage; - ULONG NonPagedPoolUsage; -} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; - -typedef struct _OBJECT_NAME_INFORMATION { - UNICODE_STRING ObjectName; -} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; - -typedef enum _OBJECT_INFORMATION_CLASS { - ObjectBasicInformation, - ObjectNameInformation, - ObjectTypeInformation, - ObjectAllInformation, - ObjectDataInformation -} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS; - -typedef struct _FILE_NAME_INFORMATION { - ULONG FileNameLength; - WCHAR FileName[1]; -} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; - -typedef enum _FILE_INFORMATION_CLASS { - // end_wdm - FileDirectoryInformation = 1, - FileFullDirectoryInformation, // 2 - FileBothDirectoryInformation, // 3 - FileBasicInformation, // 4 wdm - FileStandardInformation, // 5 wdm - FileInternalInformation, // 6 - FileEaInformation, // 7 - FileAccessInformation, // 8 - FileNameInformation, // 9 - FileRenameInformation, // 10 - FileLinkInformation, // 11 - FileNamesInformation, // 12 - FileDispositionInformation, // 13 - FilePositionInformation, // 14 wdm - FileFullEaInformation, // 15 - FileModeInformation, // 16 - FileAlignmentInformation, // 17 - FileAllInformation, // 18 - FileAllocationInformation, // 19 - FileEndOfFileInformation, // 20 wdm - FileAlternateNameInformation, // 21 - FileStreamInformation, // 22 - FilePipeInformation, // 23 - FilePipeLocalInformation, // 24 - FilePipeRemoteInformation, // 25 - FileMailslotQueryInformation, // 26 - FileMailslotSetInformation, // 27 - FileCompressionInformation, // 28 - FileObjectIdInformation, // 29 - FileCompletionInformation, // 30 - FileMoveClusterInformation, // 31 - FileQuotaInformation, // 32 - FileReparsePointInformation, // 33 - FileNetworkOpenInformation, // 34 - FileAttributeTagInformation, // 35 - FileTrackingInformation, // 36 - FileMaximumInformation - // begin_wdm -} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; - -typedef enum _SYSTEM_INFORMATION_CLASS { - SystemHandleInformation = 16 -} SYSTEM_INFORMATION_CLASS; - -typedef struct _IO_STATUS_BLOCK { - union { - NTSTATUS Status; - PVOID Pointer; - }; - ULONG_PTR Information; -} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; - -#define InitializeObjectAttributes( p, n, a, r, s ) { \ - (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ - (p)->RootDirectory = r; \ - (p)->Attributes = a; \ - (p)->ObjectName = n; \ - (p)->SecurityDescriptor = s; \ - (p)->SecurityQualityOfService = NULL; \ -} - -typedef struct _SYSTEM_HANDLE_INFORMATION { - USHORT ProcessId; - USHORT CreatorBackTraceIndex; - UCHAR ObjectTypeNumber; - UCHAR Flags; - USHORT Handle; - PVOID Object; - ACCESS_MASK GrantedAccess; -} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; - -typedef struct _SYSTEM_HANDLE_INFORMATION_EX { - ULONG NumberOfHandles; - SYSTEM_HANDLE_INFORMATION Information[1]; -} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; - -#define POBJECT_ATTRIBUTES OBJECT_ATTRIBUTES* - -typedef NTSTATUS (WINAPI* NTQUERYDIRECTORYOBJECT)( - HANDLE, - OBJDIR_INFORMATION*, - DWORD, - DWORD, - DWORD, - DWORD*, - DWORD*); - -typedef NTSTATUS (WINAPI* NTOPENDIRECTORYOBJECT)( - HANDLE *, - DWORD, - OBJECT_ATTRIBUTES* ); - -typedef NTSTATUS (WINAPI* NTGENERICOPEN) ( - OUT PHANDLE EventHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI* NTOPENEVENT)( - OUT PHANDLE EventHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI* NTOPENJOBOBJECT)( - OUT PHANDLE JobHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI* NTOPENKEYEDEVENT)( - OUT PHANDLE KeyedEventHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI* NTOPENMUTANT)( - OUT PHANDLE MutantHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI* NTOPENSECTION)( - OUT PHANDLE SectionHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI* NTOPENSEMAPHORE)( - OUT PHANDLE SemaphoreHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI* NTOPENSYMBOLICLINKOBJECT)( - OUT PHANDLE SymbolicLinkHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI* NTOPENTIMER)( - OUT PHANDLE TimerHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes); - -typedef NTSTATUS (WINAPI* NTOPENFILE)( - HANDLE *, - DWORD, - OBJECT_ATTRIBUTES *, - IO_STATUS_BLOCK *, - DWORD, - DWORD); - -typedef NTSTATUS (WINAPI* NTQUERYINFORMATIONFILE)( - HANDLE, - PIO_STATUS_BLOCK, - PVOID, - ULONG, - FILE_INFORMATION_CLASS); - -typedef NTSTATUS (WINAPI* NTQUERYSYSTEMINFORMATION)( - SYSTEM_INFORMATION_CLASS SystemInformationClass, - PVOID SystemInformation, - ULONG SystemInformationLength, - PULONG ReturnLength); - -typedef NTSTATUS (WINAPI* NTQUERYOBJECT)( - HANDLE Handle, - OBJECT_INFORMATION_CLASS ObjectInformationClass, - PVOID ObjectInformation, - ULONG ObjectInformationLength, - PULONG ReturnLength); - -typedef NTSTATUS (WINAPI* NTCLOSE) (HANDLE); - -#define DIRECTORY_QUERY 0x0001 -#define DIRECTORY_TRAVERSE 0x0002 -#define DIRECTORY_CREATE_OBJECT 0x0004 -#define DIRECTORY_CREATE_SUBDIRECTORY 0x0008 -#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) - -#endif // SANDBOX_TOOLS_FINDER_NTUNDOC_H__ diff --git a/sandbox/tools/launcher/launcher.cc b/sandbox/tools/launcher/launcher.cc deleted file mode 100644 index 8f913b3..0000000 --- a/sandbox/tools/launcher/launcher.cc +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/src/restricted_token_utils.h" - -// launcher.exe is an application used to launch another application with a -// restricted token. This is to be used for testing only. -// The parameters are the level of security of the primary token, the -// impersonation token and the job object along with the command line to -// execute. -// See the usage (launcher.exe without parameters) for the correct format. - -#define PARAM_IS(y) (argc > i) && (_wcsicmp(argv[i], y) == 0) - -void PrintUsage(const wchar_t *application_name) { - wprintf(L"\n\nUsage: \n %ls --main level --init level --job level cmd_line ", - application_name); - wprintf(L"\n\n Levels : \n\tLOCKDOWN \n\tRESTRICTED " - L"\n\tLIMITED_USER \n\tINTERACTIVE_USER \n\tNON_ADMIN \n\tUNPROTECTED"); - wprintf(L"\n\n main: Security level of the main token"); - wprintf(L"\n init: Security level of the impersonation token"); - wprintf(L"\n job: Security level of the job object"); -} - -bool GetTokenLevelFromString(const wchar_t *param, - sandbox::TokenLevel* level) { - if (_wcsicmp(param, L"LOCKDOWN") == 0) { - *level = sandbox::USER_LOCKDOWN; - } else if (_wcsicmp(param, L"RESTRICTED") == 0) { - *level = sandbox::USER_RESTRICTED; - } else if (_wcsicmp(param, L"LIMITED_USER") == 0) { - *level = sandbox::USER_LIMITED; - } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) { - *level = sandbox::USER_INTERACTIVE; - } else if (_wcsicmp(param, L"NON_ADMIN") == 0) { - *level = sandbox::USER_NON_ADMIN; - } else if (_wcsicmp(param, L"USER_RESTRICTED_SAME_ACCESS") == 0) { - *level = sandbox::USER_RESTRICTED_SAME_ACCESS; - } else if (_wcsicmp(param, L"UNPROTECTED") == 0) { - *level = sandbox::USER_UNPROTECTED; - } else { - return false; - } - - return true; -} - -bool GetJobLevelFromString(const wchar_t *param, sandbox::JobLevel* level) { - if (_wcsicmp(param, L"LOCKDOWN") == 0) { - *level = sandbox::JOB_LOCKDOWN; - } else if (_wcsicmp(param, L"RESTRICTED") == 0) { - *level = sandbox::JOB_RESTRICTED; - } else if (_wcsicmp(param, L"LIMITED_USER") == 0) { - *level = sandbox::JOB_LIMITED_USER; - } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) { - *level = sandbox::JOB_INTERACTIVE; - } else if (_wcsicmp(param, L"NON_ADMIN") == 0) { - wprintf(L"\nNON_ADMIN is not a supported job type"); - return false; - } else if (_wcsicmp(param, L"UNPROTECTED") == 0) { - *level = sandbox::JOB_UNPROTECTED; - } else { - return false; - } - - return true; -} - -int wmain(int argc, wchar_t *argv[]) { - // Extract the filename from the path. - wchar_t *app_name = wcsrchr(argv[0], L'\\'); - if (!app_name) { - app_name = argv[0]; - } else { - app_name++; - } - - // no argument - if (argc == 1) { - PrintUsage(app_name); - return -1; - } - - sandbox::TokenLevel primary_level = sandbox::USER_LOCKDOWN; - sandbox::TokenLevel impersonation_level = - sandbox::USER_RESTRICTED_SAME_ACCESS; - sandbox::JobLevel job_level = sandbox::JOB_LOCKDOWN; - ATL::CString command_line; - - // parse command line. - for (int i = 1; i < argc; ++i) { - if (PARAM_IS(L"--main")) { - i++; - if (argc > i) { - if (!GetTokenLevelFromString(argv[i], &primary_level)) { - wprintf(L"\nAbord, Unrecognized main token level \"%ls\"", argv[i]); - PrintUsage(app_name); - return -1; - } - } - } else if (PARAM_IS(L"--init")) { - i++; - if (argc > i) { - if (!GetTokenLevelFromString(argv[i], &impersonation_level)) { - wprintf(L"\nAbord, Unrecognized init token level \"%ls\"", argv[i]); - PrintUsage(app_name); - return -1; - } - } - } else if (PARAM_IS(L"--job")) { - i++; - if (argc > i) { - if (!GetJobLevelFromString(argv[i], &job_level)) { - wprintf(L"\nAbord, Unrecognized job security level \"%ls\"", argv[i]); - PrintUsage(app_name); - return -1; - } - } - } else { - if (command_line.GetLength()) { - command_line += L' '; - } - command_line += argv[i]; - } - } - - if (!command_line.GetLength()) { - wprintf(L"\nAbord, No command line specified"); - PrintUsage(app_name); - return -1; - } - - wprintf(L"\nLaunching command line: \"%ls\"\n", command_line.GetBuffer()); - - HANDLE job_handle; - DWORD err_code = sandbox::StartRestrictedProcessInJob( - command_line.GetBuffer(), - primary_level, - impersonation_level, - job_level, - &job_handle); - if (ERROR_SUCCESS != err_code) { - wprintf(L"\nAbord, Error %d while launching command line.", err_code); - return -1; - } - - wprintf(L"\nPress any key to continue."); - while(!_kbhit()) { - Sleep(100); - } - - ::CloseHandle(job_handle); - - return 0; -} diff --git a/sandbox/tools/launcher/launcher.vcproj b/sandbox/tools/launcher/launcher.vcproj deleted file mode 100644 index 71ed011..0000000 --- a/sandbox/tools/launcher/launcher.vcproj +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sandbox/win/sandbox_poc/main_ui_window.cc b/sandbox/win/sandbox_poc/main_ui_window.cc new file mode 100644 index 0000000..ef4d550 --- /dev/null +++ b/sandbox/win/sandbox_poc/main_ui_window.cc @@ -0,0 +1,668 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sandbox/sandbox_poc/main_ui_window.h" +#include "base/logging.h" +#include "sandbox/sandbox_poc/resource.h" +#include "sandbox/src/acl.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/win_utils.h" + +HWND MainUIWindow::list_view_ = NULL; + +const wchar_t MainUIWindow::kDefaultDll_[] = L"\\POCDLL.dll"; +const wchar_t MainUIWindow::kDefaultEntryPoint_[] = L"Run"; +const wchar_t MainUIWindow::kDefaultLogFile_[] = L""; + +MainUIWindow::MainUIWindow() + : instance_handle_(NULL), + spawn_target_(L""), + dll_path_(L""), + entry_point_(L""), + broker_(NULL) { +} + +MainUIWindow::~MainUIWindow() { +} + +unsigned int MainUIWindow::CreateMainWindowAndLoop( + HINSTANCE instance, + wchar_t* command_line, + int show_command, + sandbox::BrokerServices* broker) { + DCHECK(instance); + DCHECK(command_line); + DCHECK(broker); + + instance_handle_ = instance; + spawn_target_ = command_line; + broker_ = broker; + + // We'll use spawn_target_ later for creating a child process, but + // CreateProcess doesn't like double quotes, so we remove them along with + // tabs and spaces from the start and end of the string + const wchar_t *trim_removal = L" \r\t\""; + spawn_target_.erase(0, spawn_target_.find_first_not_of(trim_removal)); + spawn_target_.erase(spawn_target_.find_last_not_of(trim_removal) + 1); + + WNDCLASSEX window_class = {0}; + window_class.cbSize = sizeof(WNDCLASSEX); + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.lpfnWndProc = MainUIWindow::WndProc; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = instance; + window_class.hIcon = + ::LoadIcon(instance, MAKEINTRESOURCE(IDI_SANDBOX)); + window_class.hCursor = ::LoadCursor(NULL, IDC_ARROW); + window_class.hbrBackground = GetStockBrush(WHITE_BRUSH); + window_class.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_MAIN_UI); + window_class.lpszClassName = L"sandbox_ui_1"; + window_class.hIconSm = NULL; + + INITCOMMONCONTROLSEX controls = { + sizeof(INITCOMMONCONTROLSEX), + ICC_STANDARD_CLASSES | ICC_LISTVIEW_CLASSES + }; + ::InitCommonControlsEx(&controls); + + if (!::RegisterClassEx(&window_class)) + return ::GetLastError(); + + // Create a main window of size 600x400 + HWND window = ::CreateWindowW(window_class.lpszClassName, + L"", // window name + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, // x + CW_USEDEFAULT, // y + 600, // width + 400, // height + NULL, // parent + NULL, // NULL = use class menu + instance, + 0); // lpParam + + if (NULL == window) + return ::GetLastError(); + + ::SetWindowLongPtr(window, + GWLP_USERDATA, + reinterpret_cast(this)); + + ::SetWindowText(window, L"Sandbox Proof of Concept"); + + ::ShowWindow(window, show_command); + + MSG message; + // Now lets start the message pump retrieving messages for any window that + // belongs to the current thread + while (::GetMessage(&message, NULL, 0, 0)) { + ::TranslateMessage(&message); + ::DispatchMessage(&message); + } + + return 0; +} + +LRESULT CALLBACK MainUIWindow::WndProc(HWND window, + UINT message_id, + WPARAM wparam, + LPARAM lparam) { + MainUIWindow* host = FromWindow(window); + + #define HANDLE_MSG(hwnd, message, fn) \ + case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn)) + + switch (message_id) { + case WM_CREATE: + // 'host' is not yet available when we get the WM_CREATE message + return HANDLE_WM_CREATE(window, wparam, lparam, OnCreate); + case WM_DESTROY: + return HANDLE_WM_DESTROY(window, wparam, lparam, host->OnDestroy); + case WM_SIZE: + return HANDLE_WM_SIZE(window, wparam, lparam, host->OnSize); + case WM_COMMAND: { + // Look at which menu item was clicked on (or which accelerator) + int id = LOWORD(wparam); + switch (id) { + case ID_FILE_EXIT: + host->OnFileExit(); + break; + case ID_COMMANDS_SPAWNTARGET: + host->OnCommandsLaunch(window); + break; + default: + // Some other menu item or accelerator + break; + } + + return ERROR_SUCCESS; + } + + default: + // Some other WM_message, let it pass to DefWndProc + break; + } + + return DefWindowProc(window, message_id, wparam, lparam); +} + +INT_PTR CALLBACK MainUIWindow::SpawnTargetWndProc(HWND dialog, + UINT message_id, + WPARAM wparam, + LPARAM lparam) { + UNREFERENCED_PARAMETER(lparam); + + // Grab a reference to the main UI window (from the window handle) + MainUIWindow* host = FromWindow(GetParent(dialog)); + DCHECK(host); + + switch (message_id) { + case WM_INITDIALOG: { + // Initialize the window text for DLL name edit box + HWND edit_box_dll_name = ::GetDlgItem(dialog, IDC_DLL_NAME); + wchar_t current_dir[MAX_PATH]; + if (GetCurrentDirectory(MAX_PATH, current_dir)) { + std::wstring dll_path = std::wstring(current_dir) + + std::wstring(kDefaultDll_); + ::SetWindowText(edit_box_dll_name, dll_path.c_str()); + } + + // Initialize the window text for Entry Point edit box + HWND edit_box_entry_point = ::GetDlgItem(dialog, IDC_ENTRY_POINT); + ::SetWindowText(edit_box_entry_point, kDefaultEntryPoint_); + + // Initialize the window text for Log File edit box + HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); + ::SetWindowText(edit_box_log_file, kDefaultLogFile_); + + return static_cast(TRUE); + } + case WM_COMMAND: + // If the user presses the OK button (Launch) + if (LOWORD(wparam) == IDOK) { + if (host->OnLaunchDll(dialog)) { + if (host->SpawnTarget()) { + ::EndDialog(dialog, LOWORD(wparam)); + } + } + return static_cast(TRUE); + } else if (LOWORD(wparam) == IDCANCEL) { + // If the user presses the Cancel button + ::EndDialog(dialog, LOWORD(wparam)); + return static_cast(TRUE); + } else if (LOWORD(wparam) == IDC_BROWSE_DLL) { + // If the user presses the Browse button to look for a DLL + std::wstring dll_path = host->OnShowBrowseForDllDlg(dialog); + if (dll_path.length() > 0) { + // Initialize the window text for Log File edit box + HWND edit_box_dll_path = ::GetDlgItem(dialog, IDC_DLL_NAME); + ::SetWindowText(edit_box_dll_path, dll_path.c_str()); + } + return static_cast(TRUE); + } else if (LOWORD(wparam) == IDC_BROWSE_LOG) { + // If the user presses the Browse button to look for a log file + std::wstring log_path = host->OnShowBrowseForLogFileDlg(dialog); + if (log_path.length() > 0) { + // Initialize the window text for Log File edit box + HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); + ::SetWindowText(edit_box_log_file, log_path.c_str()); + } + return static_cast(TRUE); + } + + break; + } + + return static_cast(FALSE); +} + +MainUIWindow* MainUIWindow::FromWindow(HWND main_window) { + // We store a 'this' pointer using SetWindowLong in CreateMainWindowAndLoop + // so that we can retrieve it with this function later. This prevents us + // from having to define all the message handling functions (that we refer to + // in the window proc) as static + ::GetWindowLongPtr(main_window, GWLP_USERDATA); + return reinterpret_cast( + ::GetWindowLongPtr(main_window, GWLP_USERDATA)); +} + +BOOL MainUIWindow::OnCreate(HWND parent_window, LPCREATESTRUCT) { + // Create the listview that will the main app UI + list_view_ = ::CreateWindow(WC_LISTVIEW, // Class name + L"", // Window name + WS_CHILD | WS_VISIBLE | LVS_REPORT | + LVS_NOCOLUMNHEADER | WS_BORDER, + 0, // x + 0, // y + 0, // width + 0, // height + parent_window, // parent + NULL, // menu + ::GetModuleHandle(NULL), + 0); // lpParam + + DCHECK(list_view_); + if (!list_view_) + return FALSE; + + LVCOLUMN list_view_column = {0}; + list_view_column.mask = LVCF_FMT | LVCF_WIDTH ; + list_view_column.fmt = LVCFMT_LEFT; + list_view_column.cx = 10000; // Maximum size of an entry in the list view. + ListView_InsertColumn(list_view_, 0, &list_view_column); + + // Set list view to show green font on black background + ListView_SetBkColor(list_view_, CLR_NONE); + ListView_SetTextColor(list_view_, RGB(0x0, 0x0, 0x0)); + ListView_SetTextBkColor(list_view_, CLR_NONE); + + return TRUE; +} + +void MainUIWindow::OnDestroy(HWND window) { + UNREFERENCED_PARAMETER(window); + + // Post a quit message because our application is over when the + // user closes this window. + ::PostQuitMessage(0); +} + +void MainUIWindow::OnSize(HWND window, UINT state, int cx, int cy) { + UNREFERENCED_PARAMETER(window); + UNREFERENCED_PARAMETER(state); + + // If we have a valid inner child, resize it to cover the entire + // client area of the main UI window. + if (list_view_) { + ::MoveWindow(list_view_, + 0, // x + 0, // y + cx, // width + cy, // height + TRUE); // repaint + } +} + +void MainUIWindow::OnPaint(HWND window) { + PAINTSTRUCT paintstruct; + ::BeginPaint(window, &paintstruct); + // add painting code here if required + ::EndPaint(window, &paintstruct); +} + +void MainUIWindow::OnFileExit() { + ::PostQuitMessage(0); +} + +void MainUIWindow::OnCommandsLaunch(HWND window) { + // User wants to see the Select DLL dialog box + ::DialogBox(instance_handle_, + MAKEINTRESOURCE(IDD_LAUNCH_DLL), + window, + SpawnTargetWndProc); +} + +bool MainUIWindow::OnLaunchDll(HWND dialog) { + HWND edit_box_dll_name = ::GetDlgItem(dialog, IDC_DLL_NAME); + HWND edit_box_entry_point = ::GetDlgItem(dialog, IDC_ENTRY_POINT); + HWND edit_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE); + + wchar_t dll_path[MAX_PATH]; + wchar_t entry_point[MAX_PATH]; + wchar_t log_file[MAX_PATH]; + + int dll_name_len = ::GetWindowText(edit_box_dll_name, dll_path, MAX_PATH); + int entry_point_len = ::GetWindowText(edit_box_entry_point, + entry_point, MAX_PATH); + // Log file is optional (can be blank) + ::GetWindowText(edit_log_file, log_file, MAX_PATH); + + if (0 >= dll_name_len) { + ::MessageBox(dialog, + L"Please specify a DLL for the target to load", + L"No DLL specified", + MB_ICONERROR); + return false; + } + + if (GetFileAttributes(dll_path) == INVALID_FILE_ATTRIBUTES) { + ::MessageBox(dialog, + L"DLL specified was not found", + L"DLL not found", + MB_ICONERROR); + return false; + } + + if (0 >= entry_point_len) { + ::MessageBox(dialog, + L"Please specify an entry point for the DLL", + L"No entry point specified", + MB_ICONERROR); + return false; + } + + // store these values in the member variables for use in SpawnTarget + log_file_ = std::wstring(L"\"") + log_file + std::wstring(L"\""); + dll_path_ = dll_path; + entry_point_ = entry_point; + + return true; +} + +DWORD WINAPI MainUIWindow::ListenPipeThunk(void *param) { + return reinterpret_cast(param)->ListenPipe(); +} + +DWORD WINAPI MainUIWindow::WaitForTargetThunk(void *param) { + return reinterpret_cast(param)->WaitForTarget(); +} + +// Thread waiting for the target application to die. It displays +// a message in the list view when it happens. +DWORD MainUIWindow::WaitForTarget() { + WaitForSingleObject(target_.hProcess, INFINITE); + + DWORD exit_code = 0; + if (!GetExitCodeProcess(target_.hProcess, &exit_code)) { + exit_code = 0xFFFF; // Default exit code + } + + ::CloseHandle(target_.hProcess); + ::CloseHandle(target_.hThread); + + AddDebugMessage(L"Targed exited with return code %d", exit_code); + return 0; +} + +// Thread waiting for messages on the log pipe. It displays the messages +// in the listview. +DWORD MainUIWindow::ListenPipe() { + HANDLE logfile_handle = NULL; + ATL::CString file_to_open = log_file_.c_str(); + file_to_open.Remove(L'\"'); + if (file_to_open.GetLength()) { + logfile_handle = ::CreateFile(file_to_open.GetBuffer(), + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, // Default security attributes + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); // No template + if (INVALID_HANDLE_VALUE == logfile_handle) { + AddDebugMessage(L"Failed to open \"%ls\" for logging. Error %d", + file_to_open.GetBuffer(), ::GetLastError()); + logfile_handle = NULL; + } + } + + const int kSizeBuffer = 1024; + BYTE read_buffer[kSizeBuffer] = {0}; + ATL::CStringA read_buffer_global; + ATL::CStringA string_to_print; + + DWORD last_error = 0; + while(last_error == ERROR_SUCCESS || last_error == ERROR_PIPE_LISTENING || + last_error == ERROR_NO_DATA) + { + DWORD read_data_length; + if (::ReadFile(pipe_handle_, + read_buffer, + kSizeBuffer - 1, // Max read size + &read_data_length, + NULL)) { // Not overlapped + if (logfile_handle) { + DWORD write_data_length; + ::WriteFile(logfile_handle, + read_buffer, + read_data_length, + &write_data_length, + FALSE); // Not overlapped + } + + // Append the new buffer to the current buffer + read_buffer[read_data_length] = NULL; + read_buffer_global += reinterpret_cast(read_buffer); + read_buffer_global.Remove(10); // Remove the CRs + + // If we completed a new line, output it + int endline = read_buffer_global.Find(13); // search for LF + while (-1 != endline) { + string_to_print = read_buffer_global; + string_to_print.Delete(endline, string_to_print.GetLength()); + read_buffer_global.Delete(0, endline); + + // print the line (with the ending LF) + OutputDebugStringA(string_to_print.GetBuffer()); + + // Remove the ending LF + read_buffer_global.Delete(0, 1); + + // Add the line to the log + AddDebugMessage(L"%S", string_to_print.GetBuffer()); + + endline = read_buffer_global.Find(13); + } + last_error = ERROR_SUCCESS; + } else { + last_error = GetLastError(); + Sleep(100); + } + } + + if (read_buffer_global.GetLength()) { + AddDebugMessage(L"%S", read_buffer_global.GetBuffer()); + } + + CloseHandle(pipe_handle_); + + if (logfile_handle) { + CloseHandle(logfile_handle); + } + + return 0; +} + +bool MainUIWindow::SpawnTarget() { + // Generate the pipe name + GUID random_id; + CoCreateGuid(&random_id); + + wchar_t log_pipe[MAX_PATH] = {0}; + wnsprintf(log_pipe, MAX_PATH - 1, + L"\\\\.\\pipe\\sbox_pipe_log_%lu_%lu_%lu_%lu", + random_id.Data1, + random_id.Data2, + random_id.Data3, + random_id.Data4); + + // We concatenate the four strings, add three spaces and a zero termination + // We use the resulting string as a param to CreateProcess (in SpawnTarget) + // Documented maximum for command line in CreateProcess is 32K (msdn) + size_t size_call = spawn_target_.length() + entry_point_.length() + + dll_path_.length() + wcslen(log_pipe) + 6; + if (32 * 1024 < (size_call * sizeof(wchar_t))) { + AddDebugMessage(L"The length of the arguments exceeded 32K. " + L"Aborting operation."); + return false; + } + + wchar_t * arguments = new wchar_t[size_call]; + wnsprintf(arguments, static_cast(size_call), L"%ls %ls \"%ls\" %ls", + spawn_target_.c_str(), entry_point_.c_str(), + dll_path_.c_str(), log_pipe); + + arguments[size_call - 1] = L'\0'; + + sandbox::TargetPolicy* policy = broker_->CreatePolicy(); + policy->SetJobLevel(sandbox::JOB_LOCKDOWN, 0); + policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, + sandbox::USER_LOCKDOWN); + policy->SetAlternateDesktop(true); + policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); + + // Set the rule to allow the POC dll to be loaded by the target. Note that + // the rule allows 'all access' to the DLL, which could mean that the target + // could modify the DLL on disk. + policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_ANY, dll_path_.c_str()); + + sandbox::ResultCode result = broker_->SpawnTarget(spawn_target_.c_str(), + arguments, policy, + &target_); + + policy->Release(); + policy = NULL; + + bool return_value = false; + if (sandbox::SBOX_ALL_OK != result) { + AddDebugMessage( + L"Failed to spawn target %ls w/args (%ls), sandbox error code: %d", + spawn_target_.c_str(), arguments, result); + return_value = false; + } else { + + DWORD thread_id; + ::CreateThread(NULL, // Default security attributes + NULL, // Default stack size + &MainUIWindow::WaitForTargetThunk, + this, + 0, // No flags + &thread_id); + + pipe_handle_ = ::CreateNamedPipe(log_pipe, + PIPE_ACCESS_INBOUND | WRITE_DAC, + PIPE_TYPE_MESSAGE | PIPE_NOWAIT, + 1, // Number of instances. + 512, // Out buffer size. + 512, // In buffer size. + NMPWAIT_USE_DEFAULT_WAIT, + NULL); // Default security descriptor + + if (INVALID_HANDLE_VALUE == pipe_handle_) + AddDebugMessage(L"Failed to create pipe. Error %d", ::GetLastError()); + + if (!sandbox::AddKnownSidToKernelObject(pipe_handle_, WinWorldSid, + FILE_ALL_ACCESS)) + AddDebugMessage(L"Failed to set security on pipe. Error %d", + ::GetLastError()); + + ::CreateThread(NULL, // Default security attributes + NULL, // Default stack size + &MainUIWindow::ListenPipeThunk, + this, + 0, // No flags + &thread_id); + + ::ResumeThread(target_.hThread); + + AddDebugMessage(L"Successfully spawned target w/args (%ls)", arguments); + return_value = true; + } + + delete[] arguments; + return return_value; +} + +std::wstring MainUIWindow::OnShowBrowseForDllDlg(HWND owner) { + wchar_t filename[MAX_PATH]; + wcscpy_s(filename, MAX_PATH, L""); + + OPENFILENAMEW file_info = {0}; + file_info.lStructSize = sizeof(file_info); + file_info.hwndOwner = owner; + file_info.lpstrFile = filename; + file_info.nMaxFile = MAX_PATH; + file_info.lpstrFilter = L"DLL files (*.dll)\0*.dll\0All files\0*.*\0\0\0"; + + file_info.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + + if (GetOpenFileName(&file_info)) { + return file_info.lpstrFile; + } + + return L""; +} + +std::wstring MainUIWindow::OnShowBrowseForLogFileDlg(HWND owner) { + wchar_t filename[MAX_PATH]; + wcscpy_s(filename, MAX_PATH, L""); + + OPENFILENAMEW file_info = {0}; + file_info.lStructSize = sizeof(file_info); + file_info.hwndOwner = owner; + file_info.lpstrFile = filename; + file_info.nMaxFile = MAX_PATH; + file_info.lpstrFilter = L"Log file (*.txt)\0*.txt\0All files\0*.*\0\0\0"; + + file_info.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; + + if (GetSaveFileName(&file_info)) { + return file_info.lpstrFile; + } + + return L""; +} + +void MainUIWindow::AddDebugMessage(const wchar_t* format, ...) { + DCHECK(format); + if (!format) + return; + + const int kMaxDebugBuffSize = 1024; + + va_list arg_list; + _crt_va_start(arg_list, format); + + wchar_t text[kMaxDebugBuffSize + 1]; + vswprintf_s(text, kMaxDebugBuffSize, format, arg_list); + text[kMaxDebugBuffSize] = L'\0'; + + InsertLineInListView(text); +} + + +void MainUIWindow::InsertLineInListView(wchar_t* debug_message) { + DCHECK(debug_message); + if (!debug_message) + return; + + // Prepend the time to the message + const int kSizeTime = 100; + size_t size_message_with_time = wcslen(debug_message) + kSizeTime; + wchar_t * message_time = new wchar_t[size_message_with_time]; + + time_t time_temp; + time_temp = time(NULL); + + struct tm time = {0}; + localtime_s(&time, &time_temp); + + size_t return_code; + return_code = wcsftime(message_time, kSizeTime, L"[%H:%M:%S] ", &time); + + wcscat_s(message_time, size_message_with_time, debug_message); + + // We add the debug message to the top of the listview + LVITEM item; + item.iItem = ListView_GetItemCount(list_view_); + item.iSubItem = 0; + item.mask = LVIF_TEXT | LVIF_PARAM; + item.pszText = message_time; + item.lParam = 0; + + ListView_InsertItem(list_view_, &item); + + delete[] message_time; +} diff --git a/sandbox/win/sandbox_poc/main_ui_window.h b/sandbox/win/sandbox_poc/main_ui_window.h new file mode 100644 index 0000000..c70189a --- /dev/null +++ b/sandbox/win/sandbox_poc/main_ui_window.h @@ -0,0 +1,193 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__ +#define SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__ + +#include + +#include "base/basictypes.h" + +namespace sandbox { +class BrokerServices; +enum ResultCode; +} + +// Header file for the MainUIWindow, a simple window with a menu bar that +// can pop up a dialog (accessible through the menu bar), to specify a path and +// filename of any DLL to load and choose an entry point of his/her choice +// (note: only entry points with no parameters are expected to work). +// +// The purpose of this is to be able to spawn an EXE inside a SandBox, have it +// load a DLL and call the entry point on it to test how it behaves inside the +// sandbox. This is useful for developer debugging and for security testing. +// +// The MainUIWindow also has a listview that displays debugging information to +// the user. +// +// Sample usage: +// +// MainUIWindow window; +// unsigned int ret = window.CreateMainWindowAndLoop( +// handle_to_current_instance, +// ::GetCommandLineW(), +// show_command, +// broker); +// +// The CreateMainWindowAndLoop() contains a message loop that ends when the +// user closes the MainUIWindow. + +// This class encapsulates the Main UI window for the broker application. +// It simply shows a menu that gives the user the ability (through a dialog) to +// specify a DLL and what entry point to call in the DLL. +class MainUIWindow { + public: + MainUIWindow(); + ~MainUIWindow(); + + // Creates the main window, displays it and starts the message pump. This + // call will not return until user closes the main UI window that appears + // as a result. Arguments 'instance', 'command_line' and 'show_cmd' can be + // passed in directly from winmain. The 'broker' argument is a pointer to a + // BrokerService that will launch a new EXE inside the sandbox and load the + // DLL of the user's choice. + unsigned int CreateMainWindowAndLoop(HINSTANCE instance, + wchar_t* command_line, + int show_command, + sandbox::BrokerServices* broker); + + private: + // The default value DLL name to add to the edit box. + static const wchar_t kDefaultDll_[]; + + // The default value to show in the entry point. + static const wchar_t kDefaultEntryPoint_[]; + + // The default value to show in the log file. + static const wchar_t kDefaultLogFile_[]; + + // Handles the messages sent to the main UI window. The return value is the + // result of the message processing and depends on the message. + static LRESULT CALLBACK WndProc(HWND window, + UINT message_id, + WPARAM wparam, + LPARAM lparam); + + // Handles the messages sent to the SpawnTarget dialog. The return value is + // the result of the message processing and depends on the message. + static INT_PTR CALLBACK SpawnTargetWndProc(HWND dialog, + UINT message_id, + WPARAM wparam, + LPARAM lparam); + + // Retrieves a pointer to the MainWindow from a value stored along with the + // window handle (passed in as hwnd). Return value is a pointer to the + // MainUIWindow previously stored with SetWindowLong() during WM_CREATE. + static MainUIWindow* FromWindow(HWND main_window); + + // Handles the WM_CREATE message for the main UI window. Returns TRUE on + // success. + static BOOL OnCreate(HWND parent_window, LPCREATESTRUCT); + + // Handles the WM_DESTROY message for the main UI window. + void OnDestroy(HWND window); + + // Handles the WM_SIZE message for the main UI window. + void OnSize(HWND window, UINT state, int cx, int cy); + + // Handles the WM_PAINT message for the main UI window. + void OnPaint(HWND window); + + // Handles the menu command File \ Exit for the main UI window. + void OnFileExit(); + + // Handles the menu command Commands \ Launch for the main UI window. + void OnCommandsLaunch(HWND window); + + // Handles the Launch button in the SpawnTarget dialog (normally clicked + // after selecting DLL and entry point). OnLaunchDll will retrieve the + // values entered by the user and store it in the members of the class. + // Returns true if user selected a non-zero values for DLL filename + // (possibly including path also) and entry point. + bool OnLaunchDll(HWND dialog); + + // Spawns a target EXE inside the sandbox (with the help of the + // BrokerServices passed in to CreateMainWindowAndLoop), and passes to it + // (as command line arguments) the DLL path and the entry point function + // name. The EXE is expected to parse the command line and load the DLL. + // NOTE: The broker does not know if the target EXE successfully loaded the + // DLL, for that you have to rely on the EXE providing a log. + // Returns true if the broker reports that it was successful in creating + // the target and false if not. + bool SpawnTarget(); + + // Shows a standard File Open dialog and returns the DLL filename selected or + // blank string if the user cancelled (or an error occurred). + std::wstring OnShowBrowseForDllDlg(HWND owner); + + // Shows a standard Save As dialog and returns the log filename selected or + // blank string if the user cancelled (or an error occurred). + std::wstring OnShowBrowseForLogFileDlg(HWND owner); + + // Formats a message using the supplied format string and prints it in the + // listview in the main UI window. Passing a NULL param in 'fmt' results in + // no action being performed. Maximum message length is 1K. + void AddDebugMessage(const wchar_t* format, ...); + + // Assists AddDebugMessage in displaying a message in the ListView. It + // simply wraps ListView_InsertItem to insert a debugging message to the + // top of the list view. Passing a NULL param in 'fmt' results in no action + // being performed. + void InsertLineInListView(wchar_t* debug_message); + + // Calls ListenPipe using the class instance received in parameter. This is + // used to create new threads executing ListenPipe + static DWORD WINAPI ListenPipeThunk(void *param); + + // Calls WaitForTargetThunk using the class instance received in parameter + // This is used to create new threads executing WaitForTarget. + static DWORD WINAPI WaitForTargetThunk(void *param); + + // Listens on a pipe and output the data received to a file and to the UI. + DWORD ListenPipe(); + + // Waits for the target to dies and display a message in the UI. + DWORD WaitForTarget(); + + // The BrokerServices will be used to spawn an EXE in a sandbox and ask + // it to load a DLL. + sandbox::BrokerServices* broker_; + + // Contains the information about the running target. + PROCESS_INFORMATION target_; + + // This is essentially a command line to a target executable that the + // broker will spawn and ask to load the DLL. + std::wstring spawn_target_; + + // A handle to the current instance of the app. Passed in to this class + // through CreateMainWindowAndLoop. + HINSTANCE instance_handle_; + + // A path to the DLL that the target should load once it executes. + std::wstring dll_path_; + + // The name of the entry point the target should call after it loads the DLL. + std::wstring entry_point_; + + // The name of the log file to use. + std::wstring log_file_; + + // This is a static handle to the list view that fills up the entire main + // UI window. The list view is used to display debugging information to the + // user. + static HWND list_view_; + + // Pipe used to communicate the logs between the target and the broker. + HANDLE pipe_handle_; + + DISALLOW_COPY_AND_ASSIGN(MainUIWindow); +}; + +#endif // SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__ diff --git a/sandbox/win/sandbox_poc/pocdll/exports.h b/sandbox/win/sandbox_poc/pocdll/exports.h new file mode 100644 index 0000000..66a07d6 --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/exports.h @@ -0,0 +1,89 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SANDBOX_POC_POCDLL_EXPORTS_H__ +#define SANDBOX_SANDBOX_POC_POCDLL_EXPORTS_H__ + +#include + +#ifdef POCDLL_EXPORTS +#define POCDLL_API __declspec(dllexport) __cdecl +#else +#define POCDLL_API __declspec(dllimport) __cdecl +#endif + +extern "C" { +// Tries to open several known system path and outputs +// the result. +// "log" is the handle of the log file. +void POCDLL_API TestFileSystem(HANDLE log); + +// Tries to find all handles open in the process and prints the name of the +// resource references by the handle along with the access right. +// "log" is the handle of the log file. +void POCDLL_API TestGetHandle(HANDLE log); + +// Creates a lot of threads until it cannot create more. The goal of this +// function is to determine if it's possible to crash the machine when we +// flood the machine with new threads +// "log" is the handle of the log file. +void POCDLL_API TestThreadBombing(HANDLE log); + +// Takes all cpu of the machine. For each processor on the machine we assign +// a thread. This thread will compute a mathematical expression over and over +// to take all cpu. +// "log" is the handle of the log file. +// Note: here we are using the affinity to find out how many processors are on +// the machine and to force a thread to run only on a given processor. +void POCDLL_API TestTakeAllCpu(HANDLE log); + +// Creates memory in the heap until it fails 5 times in a row and prints the +// amount of memory created. This function is used to find out if it's possible +// to take all memory on the machine and crash the system. +// "log" is the handle of the log file. +void POCDLL_API TestUseAllMemory(HANDLE log); + +// Creates millions of kernel objects. This function is used to find out if it's +// possible to crash the system if we create too many kernel objects and if we +// hold too many handles. All those kernel objects are unnamed. +// "log" is the handle of the log file. +void POCDLL_API TestCreateObjects(HANDLE log); + +// Receives a hwnd and tries to close it. This is the callback for EnumWindows. +// It will be called for each window(hwnd) on the system. +// "log" is the handle of the log file. +// Always returns TRUE to tell the system that we want to continue the +// enumeration. +void POCDLL_API TestCloseHWND(HANDLE log); + +// Tries to listen on the port 88. +// "log" is the handle of the log file. +void POCDLL_API TestNetworkListen(HANDLE log); + +// Lists all processes on the system and tries to open them +// "log" is the handle of the log file. +void POCDLL_API TestProcesses(HANDLE log); + +// Lists all threads on the system and tries to open them +// "log" is the handle of the log file. +void POCDLL_API TestThreads(HANDLE log); + +// Tries to open some known system registry key and outputs the result. +// "log" is the handle of the log file. +void POCDLL_API TestRegistry(HANDLE log); + +// Records all keystrokes typed for 15 seconds and then display them. +// "log" is the handle of the log file. +void POCDLL_API TestSpyKeys(HANDLE log); + +// Tries to read pixels on the monitor and output if the operation +// failes or succeeded. +// "log" is the handle of the log file. +void POCDLL_API TestSpyScreen(HANDLE log); + +// Runs all tests except those who are invasive +void POCDLL_API Run(HANDLE log); +} + +#endif // SANDBOX_SANDBOX_POC_POCDLL_EXPORTS_H__ diff --git a/sandbox/win/sandbox_poc/pocdll/fs.cc b/sandbox/win/sandbox_poc/pocdll/fs.cc new file mode 100644 index 0000000..42c1006 --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/fs.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/sandbox_poc/pocdll/exports.h" +#include "sandbox/sandbox_poc/pocdll/utils.h" + +// This file contains the tests used to verify the security of the file system. + +// Tries to open a file and outputs the result. +// "path" can contain environment variables. +// "output" is the stream for the logging. +void TryOpenFile(wchar_t *path, FILE *output) { + wchar_t path_expanded[MAX_PATH] = {0}; + DWORD size = ::ExpandEnvironmentStrings(path, path_expanded, MAX_PATH - 1); + if (!size) { + fprintf(output, "[ERROR] Cannot expand \"%S\". Error %S.\r\n", path, + ::GetLastError()); + } + + HANDLE file; + file = ::CreateFile(path_expanded, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, // No security attributes + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); // No template + + if (file && INVALID_HANDLE_VALUE != file) { + fprintf(output, "[GRANTED] Opening file \"%S\". Handle 0x%p\r\n", path, + file); + ::CloseHandle(file); + } else { + fprintf(output, "[BLOCKED] Opening file \"%S\". Error %d.\r\n", path, + ::GetLastError()); + } +} + +void POCDLL_API TestFileSystem(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + TryOpenFile(L"%SystemDrive%", output); + TryOpenFile(L"%SystemRoot%", output); + TryOpenFile(L"%ProgramFiles%", output); + TryOpenFile(L"%SystemRoot%\\System32", output); + TryOpenFile(L"%SystemRoot%\\explorer.exe", output); + TryOpenFile(L"%SystemRoot%\\Cursors\\arrow_i.cur", output); + TryOpenFile(L"%AllUsersProfile%", output); + TryOpenFile(L"%UserProfile%", output); + TryOpenFile(L"%Temp%", output); + TryOpenFile(L"%AppData%", output); +} diff --git a/sandbox/win/sandbox_poc/pocdll/handles.cc b/sandbox/win/sandbox_poc/pocdll/handles.cc new file mode 100644 index 0000000..05a57b7 --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/handles.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/sandbox_poc/pocdll/exports.h" +#include "sandbox/sandbox_poc/pocdll/utils.h" +#include "sandbox/tools/finder/ntundoc.h" + +// This file contains the tests used to verify the security of handles in +// the process + +NTQUERYOBJECT NtQueryObject; +NTQUERYINFORMATIONFILE NtQueryInformationFile; +NTQUERYSYSTEMINFORMATION NtQuerySystemInformation; + +void POCDLL_API TestGetHandle(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + // Initialize the NTAPI functions we need + HMODULE ntdll_handle = ::GetModuleHandle(L"ntdll.dll"); + if (!ntdll_handle) { + fprintf(output, "[ERROR] Cannot load ntdll.dll. Error %d\r\n", + ::GetLastError()); + return; + } + + NtQueryObject = reinterpret_cast( + GetProcAddress(ntdll_handle, "NtQueryObject")); + NtQueryInformationFile = reinterpret_cast( + GetProcAddress(ntdll_handle, "NtQueryInformationFile")); + NtQuerySystemInformation = reinterpret_cast( + GetProcAddress(ntdll_handle, "NtQuerySystemInformation")); + + if (!NtQueryObject || !NtQueryInformationFile || !NtQuerySystemInformation) { + fprintf(output, "[ERROR] Cannot load all NT functions. Error %d\r\n", + ::GetLastError()); + return; + } + + // Get the number of handles on the system + DWORD buffer_size = 0; + SYSTEM_HANDLE_INFORMATION_EX temp_info; + NTSTATUS status = NtQuerySystemInformation( + SystemHandleInformation, &temp_info, sizeof(temp_info), + &buffer_size); + if (!buffer_size) { + fprintf(output, "[ERROR] Get the number of handles. Error 0x%X\r\n", + status); + return; + } + + SYSTEM_HANDLE_INFORMATION_EX *system_handles = + reinterpret_cast(new BYTE[buffer_size]); + + status = NtQuerySystemInformation(SystemHandleInformation, system_handles, + buffer_size, &buffer_size); + if (STATUS_SUCCESS != status) { + fprintf(output, "[ERROR] Failed to get the handle list. Error 0x%X\r\n", + status); + delete [] system_handles; + return; + } + + for (ULONG i = 0; i < system_handles->NumberOfHandles; ++i) { + USHORT h = system_handles->Information[i].Handle; + if (system_handles->Information[i].ProcessId != ::GetCurrentProcessId()) + continue; + + OBJECT_NAME_INFORMATION *name = NULL; + ULONG name_size = 0; + // Query the name information a first time to get the size of the name. + status = NtQueryObject(reinterpret_cast(h), + ObjectNameInformation, + name, + name_size, + &name_size); + + if (name_size) { + name = reinterpret_cast(new BYTE[name_size]); + + // Query the name information a second time to get the name of the + // object referenced by the handle. + status = NtQueryObject(reinterpret_cast(h), + ObjectNameInformation, + name, + name_size, + &name_size); + } + + PUBLIC_OBJECT_TYPE_INFORMATION *type = NULL; + ULONG type_size = 0; + + // Query the object to get the size of the object type name. + status = NtQueryObject(reinterpret_cast(h), + ObjectTypeInformation, + type, + type_size, + &type_size); + if (type_size) { + type = reinterpret_cast( + new BYTE[type_size]); + + // Query the type information a second time to get the object type + // name. + status = NtQueryObject(reinterpret_cast(h), + ObjectTypeInformation, + type, + type_size, + &type_size); + } + + // NtQueryObject cannot return the name for a file. In this case we + // need to ask NtQueryInformationFile + FILE_NAME_INFORMATION *file_name = NULL; + if (type && wcsncmp(L"File", type->TypeName.Buffer, + (type->TypeName.Length / + sizeof(type->TypeName.Buffer[0]))) == 0) { + // This function does not return the size of the buffer. We need to + // iterate and always increase the buffer size until the function + // succeeds. (Or at least does not fail with STATUS_BUFFER_OVERFLOW) + ULONG size_file = MAX_PATH; + IO_STATUS_BLOCK status_block = {0}; + do { + // Delete the previous buffer create. The buffer was too small + if (file_name) { + delete[] reinterpret_cast(file_name); + file_name = NULL; + } + + // Increase the buffer and do the call agan + size_file += MAX_PATH; + file_name = reinterpret_cast( + new BYTE[size_file]); + status = NtQueryInformationFile(reinterpret_cast(h), + &status_block, + file_name, + size_file, + FileNameInformation); + } while (status == STATUS_BUFFER_OVERFLOW); + + if (STATUS_SUCCESS != status) { + if (file_name) { + delete[] file_name; + file_name = NULL; + } + } + } + + if (file_name) { + UNICODE_STRING file_name_string; + file_name_string.Buffer = file_name->FileName; + file_name_string.Length = (USHORT)file_name->FileNameLength; + file_name_string.MaximumLength = (USHORT)file_name->FileNameLength; + fprintf(output, "[GRANTED] Handle 0x%4.4X Access: 0x%8.8X " + "Type: %-13.13wZ Path: %wZ\r\n", + h, + system_handles->Information[i].GrantedAccess, + type ? &type->TypeName : NULL, + &file_name_string); + } else { + fprintf(output, "[GRANTED] Handle 0x%4.4X Access: 0x%8.8X " + "Type: %-13.13wZ Path: %wZ\r\n", + h, + system_handles->Information[i].GrantedAccess, + type ? &type->TypeName : NULL, + name ? &name->ObjectName : NULL); + } + + if (type) { + delete[] type; + } + + if (file_name) { + delete[] file_name; + } + + if (name) { + delete [] name; + } + } + + if (system_handles) { + delete [] system_handles; + } +} diff --git a/sandbox/win/sandbox_poc/pocdll/invasive.cc b/sandbox/win/sandbox_poc/pocdll/invasive.cc new file mode 100644 index 0000000..1bac7c1 --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/invasive.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "sandbox/sandbox_poc/pocdll/exports.h" +#include "sandbox/sandbox_poc/pocdll/utils.h" + +// This file contains the tests used to verify if it's possible to DOS or crash +// the machine. All tests that can impact the stability of the machine should +// be in this file. + +// Sleeps forever. this function is used to be the +// entry point for the threads created by the thread bombing function. +// This function never returns. +DWORD WINAPI MyThreadBombimgFunction(void *param) { + UNREFERENCED_PARAMETER(param); + Sleep(INFINITE); + return 0; +} + +void POCDLL_API TestThreadBombing(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + // we stop after 5 errors in a row + int number_errors = 0; + for (int i = 0; i < 100000; ++i) { + DWORD tid; + // Create the thread and leak the handle. + HANDLE thread = ::CreateThread(NULL, // Default security attributes + NULL, // Stack size + MyThreadBombimgFunction, + NULL, // Parameter + 0, // No creation flags + &tid); + if (thread) { + fprintf(output, "[GRANTED] Creating thread with tid 0x%X\r\n", tid); + ::CloseHandle(thread); + number_errors = 0; + } else { + fprintf(output, "[BLOCKED] Creating thread. Error %d\r\n", + ::GetLastError()); + number_errors++; + } + + if (number_errors >= 5) { + break; + } + } +} + + +// Executes a complex mathematical operation forever in a loop. This function +// is used as entry point for the threads created by TestTakeAllCpu. It it +// designed to take all CPU on the processor where the thread is running. +// The return value is always 0. +DWORD WINAPI TakeAllCpu(void *param) { + UNREFERENCED_PARAMETER(param); + int cpt = 0; + for (;;) { + cpt += 2; + cpt /= 2; + cpt *= cpt; + cpt = cpt % 100; + cpt = cpt | (cpt * cpt); + } +} + +void POCDLL_API TestTakeAllCpu(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + DWORD_PTR process_mask = 0; + DWORD_PTR system_mask = 0; + if (::GetProcessAffinityMask(::GetCurrentProcess(), + &process_mask, + &system_mask)) { + DWORD_PTR affinity_mask = 1; + + while (system_mask) { + DWORD tid = 0; + + HANDLE thread = ::CreateThread(NULL, // Default security attributes. + NULL, // Stack size. + TakeAllCpu, + NULL, // Parameter. + 0, // No creation flags. + &tid); + ::SetThreadAffinityMask(thread, affinity_mask); + + if (::SetThreadPriority(thread, REALTIME_PRIORITY_CLASS)) { + fprintf(output, "[GRANTED] Set thread(%d) priority to Realtime\r\n", + tid); + } else { + fprintf(output, "[BLOCKED] Set thread(%d) priority to Realtime\r\n", + tid); + } + + ::CloseHandle(thread); + + affinity_mask = affinity_mask << 1; + system_mask = system_mask >> 1; + } + } else { + fprintf(output, "[ERROR] Cannot get affinity mask. Error %d\r\n", + ::GetLastError()); + } +} + +void POCDLL_API TestUseAllMemory(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + int number_errors = 0; + unsigned long memory_size = 0; + for (;;) { + DWORD *ptr_to_leak = reinterpret_cast(malloc(1024*256)); + if (ptr_to_leak) { + memory_size += (256); + number_errors = 0; + } else { + number_errors++; + } + + // check if we have more than 5 errors in a row. If so, quit. + if (number_errors >= 5) { + fprintf(output, "[INFO] Created %lu kb of memory\r\n", memory_size); + return; + } + + Sleep(5); // 5ms to be able to see the progression easily with taskmgr. + } +} + +void POCDLL_API TestCreateObjects(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + int mutexes = 0; + int jobs = 0; + int events = 0; + for (int i = 0; i < 1000000; ++i) { + if (::CreateMutex(NULL, // Default security attributes. + TRUE, // We are the initial owner. + NULL)) { // No name. + mutexes++; + } + + if (::CreateJobObject(NULL, // Default security attributes. + NULL)) { // No name. + jobs++; + } + + if (::CreateEvent(NULL, // Default security attributes. + TRUE, // Manual Reset. + TRUE, // Object is signaled. + NULL)) { // No name. + events++; + } + } + + fprintf(output, "[GRANTED] Created %d mutexes, %d jobs and %d events for " + "a total of %d objects out of 3 000 000\r\n", mutexes, jobs, + events, mutexes + jobs + events); +} + +BOOL CALLBACK EnumWindowCallback(HWND hwnd, LPARAM output) { + DWORD pid; + ::GetWindowThreadProcessId(hwnd, &pid); + if (pid != ::GetCurrentProcessId()) { + wchar_t window_title[100 + 1] = {0}; + ::GetWindowText(hwnd, window_title, 100); + fprintf(reinterpret_cast(output), + "[GRANTED] Found window 0x%p with title %S\r\n", + hwnd, + window_title); + ::CloseWindow(hwnd); + } + + return TRUE; +} + +// Enumerates all the windows on the system and call the function to try to +// close them. The goal of this function is to try to kill the system by +// closing all windows. +// "output" is the stream used for logging. +void POCDLL_API TestCloseHWND(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + ::EnumWindows(EnumWindowCallback, PtrToLong(output)); + // TODO(nsylvain): find a way to know when the enum is finished + // before returning. + ::Sleep(3000); +} diff --git a/sandbox/win/sandbox_poc/pocdll/network.cc b/sandbox/win/sandbox_poc/pocdll/network.cc new file mode 100644 index 0000000..09e9f33 --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/network.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/sandbox_poc/pocdll/exports.h" +#include "sandbox/sandbox_poc/pocdll/utils.h" + +// This file contains the tests used to verify the security of the network. + +void POCDLL_API TestNetworkListen(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); +#if DONT_WANT_INTERCEPTIONS_JUST_WANT_NETWORK + // Initialize Winsock + WSADATA wsa_data; + int result = ::WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (result != NO_ERROR) { + fprintf(output, "[ERROR] Cannot initialize winsock. Error%d\r\n", result); + return; + } + + // Create a SOCKET for listening for + // incoming connection requests. + SOCKET listen_socket; + listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listen_socket == INVALID_SOCKET) { + fprintf(output, "[ERROR] Failed to create socket. Error %ld\r\n", + ::WSAGetLastError()); + ::WSACleanup(); + return; + } + + // The sockaddr_in structure specifies the address family, + // IP address, and port for the socket that is being bound. + sockaddr_in service; + service.sin_family = AF_INET; + service.sin_addr.s_addr = inet_addr("127.0.0.1"); + service.sin_port = htons(88); + + if (bind(listen_socket, reinterpret_cast(&service), + sizeof(service)) == SOCKET_ERROR) { + fprintf(output, "[BLOCKED] Bind socket on port 88. Error %ld\r\n", + ::WSAGetLastError()); + closesocket(listen_socket); + ::WSACleanup(); + return; + } + + // Listen for incoming connection requests + // on the created socket + if (listen(listen_socket, SOMAXCONN) == SOCKET_ERROR) { + fprintf(output, "[BLOCKED] Listen socket on port 88. Error %ld\r\n", + ::WSAGetLastError()); + + } else { + fprintf(output, "[GRANTED] Listen socket on port 88.\r\n", + ::WSAGetLastError()); + } + + ::WSACleanup(); + return; +#else // DONT_WANT_INTERCEPTIONS_JUST_WANT_NETWORK + // Just print out that this test is not running. + fprintf(output, "[ERROR] No network tests.\r\n"); +#endif // DONT_WANT_INTERCEPTIONS_JUST_WANT_NETWORK +} diff --git a/sandbox/win/sandbox_poc/pocdll/pocdll.cc b/sandbox/win/sandbox_poc/pocdll/pocdll.cc new file mode 100644 index 0000000..3387064 --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/pocdll.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#include "sandbox/sandbox_poc/pocdll/exports.h" +#include "sandbox/sandbox_poc/pocdll/utils.h" + +BOOL APIENTRY DllMain(HMODULE module, + DWORD reason_for_call, + LPVOID reserved) { + UNREFERENCED_PARAMETER(module); + UNREFERENCED_PARAMETER(reason_for_call); + UNREFERENCED_PARAMETER(reserved); + return TRUE; +} + +void POCDLL_API Run(HANDLE log) { + TestFileSystem(log); + TestRegistry(log); + TestNetworkListen(log); + TestSpyScreen(log); + TestSpyKeys(log); + TestThreads(log); + TestProcesses(log); + TestGetHandle(log); +} diff --git a/sandbox/win/sandbox_poc/pocdll/pocdll.vcproj b/sandbox/win/sandbox_poc/pocdll/pocdll.vcproj new file mode 100644 index 0000000..8e4e31f --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/pocdll.vcproj @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc b/sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc new file mode 100644 index 0000000..1bef005 --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include "sandbox/sandbox_poc/pocdll/exports.h" +#include "sandbox/sandbox_poc/pocdll/utils.h" + +// This file contains the tests used to verify the security of threads and +// processes. + +void POCDLL_API TestProcesses(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + if (INVALID_HANDLE_VALUE == snapshot) { + fprintf(output, "[BLOCKED] Cannot list all processes on the system. " + "Error %d\r\n", ::GetLastError()); + return; + } + + PROCESSENTRY32 process_entry = {0}; + process_entry.dwSize = sizeof(PROCESSENTRY32); + + BOOL result = ::Process32First(snapshot, &process_entry); + + while (result) { + HANDLE process = ::OpenProcess(PROCESS_VM_READ, + FALSE, // Do not inherit handle. + process_entry.th32ProcessID); + if (NULL == process) { + fprintf(output, "[BLOCKED] Found process %S:%d but cannot open it. " + "Error %d\r\n", + process_entry.szExeFile, + process_entry.th32ProcessID, + ::GetLastError()); + } else { + fprintf(output, "[GRANTED] Found process %S:%d and open succeeded.\r\n", + process_entry.szExeFile, process_entry.th32ProcessID); + ::CloseHandle(process); + } + + result = ::Process32Next(snapshot, &process_entry); + } + + DWORD err_code = ::GetLastError(); + if (ERROR_NO_MORE_FILES != err_code) { + fprintf(output, "[ERROR] Error %d while looking at the processes on " + "the system\r\n", err_code); + } + + ::CloseHandle(snapshot); +} + +void POCDLL_API TestThreads(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL); + if (INVALID_HANDLE_VALUE == snapshot) { + fprintf(output, "[BLOCKED] Cannot list all threads on the system. " + "Error %d\r\n", ::GetLastError()); + return; + } + + THREADENTRY32 thread_entry = {0}; + thread_entry.dwSize = sizeof(THREADENTRY32); + + BOOL result = ::Thread32First(snapshot, &thread_entry); + int nb_success = 0; + int nb_failure = 0; + + while (result) { + HANDLE thread = ::OpenThread(THREAD_QUERY_INFORMATION, + FALSE, // Do not inherit handles. + thread_entry.th32ThreadID); + if (NULL == thread) { + nb_failure++; + } else { + nb_success++; + fprintf(output, "[GRANTED] Found thread %d:%d and able to open it.\r\n", + thread_entry.th32OwnerProcessID, + thread_entry.th32ThreadID); + ::CloseHandle(thread); + } + + result = Thread32Next(snapshot, &thread_entry); + } + + DWORD err_code = ::GetLastError(); + if (ERROR_NO_MORE_FILES != err_code) { + fprintf(output, "[ERROR] Error %d while looking at the processes on " + "the system\r\n", err_code); + } + + fprintf(output, "[INFO] Found %d threads. Able to open %d of them\r\n", + nb_success + nb_failure, nb_success); + + ::CloseHandle(snapshot); +} diff --git a/sandbox/win/sandbox_poc/pocdll/registry.cc b/sandbox/win/sandbox_poc/pocdll/registry.cc new file mode 100644 index 0000000..f5b249d --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/registry.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/sandbox_poc/pocdll/exports.h" +#include "sandbox/sandbox_poc/pocdll/utils.h" + +// This file contains the tests used to verify the security of the registry. + +// Converts an HKEY to a string. This is using the lazy way and works only +// for the main hives. +// "key" is the hive to convert to string. +// The return value is the string corresponding to the hive or "unknown" +const wchar_t *HKEYToString(const HKEY key) { + switch (reinterpret_cast(key)) { + case HKEY_CLASSES_ROOT: + return L"HKEY_CLASSES_ROOT"; + case HKEY_CURRENT_CONFIG: + return L"HKEY_CURRENT_CONFIG"; + case HKEY_CURRENT_USER: + return L"HKEY_CURRENT_USER"; + case HKEY_LOCAL_MACHINE: + return L"HKEY_LOCAL_MACHINE"; + case HKEY_USERS: + return L"HKEY_USERS"; + } + return L"unknown"; +} + +// Tries to open the key hive\path and outputs the result. +// "output" is the stream used for logging. +void TryOpenKey(const HKEY hive, const wchar_t *path, FILE *output) { + HKEY key; + LONG err_code = ::RegOpenKeyEx(hive, + path, + 0, // Reserved, must be 0. + MAXIMUM_ALLOWED, + &key); + if (ERROR_SUCCESS == err_code) { + fprintf(output, "[GRANTED] Opening key \"%S\\%S\". Handle 0x%p\r\n", + HKEYToString(hive), + path, + key); + ::RegCloseKey(key); + } else { + fprintf(output, "[BLOCKED] Opening key \"%S\\%S\". Error %d\r\n", + HKEYToString(hive), + path, + err_code); + } +} + +void POCDLL_API TestRegistry(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + TryOpenKey(HKEY_LOCAL_MACHINE, NULL, output); + TryOpenKey(HKEY_CURRENT_USER, NULL, output); + TryOpenKey(HKEY_USERS, NULL, output); + TryOpenKey(HKEY_LOCAL_MACHINE, + L"Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon", + output); +} diff --git a/sandbox/win/sandbox_poc/pocdll/spyware.cc b/sandbox/win/sandbox_poc/pocdll/spyware.cc new file mode 100644 index 0000000..b9bf64a --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/spyware.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "sandbox/sandbox_poc/pocdll/exports.h" +#include "sandbox/sandbox_poc/pocdll/utils.h" + +// This file contains the tests used to verify the security of the system by +// using some spying techniques. + +void POCDLL_API TestSpyKeys(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + if (RegisterHotKey(NULL, 1, 0, 0x42)) { + fprintf(output, "[GRANTED] successfully registered hotkey\r\n"); + UnregisterHotKey(NULL, 1); + } else { + fprintf(output, "[BLOCKED] Failed to register hotkey. Error = %d\r\n", + ::GetLastError()); + } + + fprintf(output, "[INFO] Logging keystrokes for 15 seconds\r\n"); + fflush(output); + std::wstring logged; + DWORD tick = ::GetTickCount() + 15000; + while (tick > ::GetTickCount()) { + for (int i = 0; i < 256; ++i) { + if (::GetAsyncKeyState(i) & 1) { + if (i >= VK_SPACE && i <= 0x5A /*VK_Z*/) { + logged.append(1, static_cast(i)); + } else { + logged.append(1, '?'); + } + } + } + } + + if (logged.size()) { + fprintf(output, "[GRANTED] Spyed keystrokes \"%S\"\r\n", + logged.c_str()); + } else { + fprintf(output, "[BLOCKED] Spyed keystrokes \"(null)\"\r\n"); + } +} + +void POCDLL_API TestSpyScreen(HANDLE log) { + HandleToFile handle2file; + FILE *output = handle2file.Translate(log, "w"); + + HDC screen_dc = ::GetDC(NULL); + COLORREF pixel_color = ::GetPixel(screen_dc, 0, 0); + + for (int x = 0; x < 10; ++x) { + for (int y = 0; y < 10; ++y) { + if (::GetPixel(screen_dc, x, y) != pixel_color) { + fprintf(output, "[GRANTED] Read pixel on screen\r\n"); + return; + } + } + } + + fprintf(output, "[BLOCKED] Read pixel on screen. Error = %d\r\n", + ::GetLastError()); +} diff --git a/sandbox/win/sandbox_poc/pocdll/utils.h b/sandbox/win/sandbox_poc/pocdll/utils.h new file mode 100644 index 0000000..ae42861 --- /dev/null +++ b/sandbox/win/sandbox_poc/pocdll/utils.h @@ -0,0 +1,65 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SANDBOX_POC_POCDLL_UTILS_H__ +#define SANDBOX_SANDBOX_POC_POCDLL_UTILS_H__ + +#include +#include +#include "base/basictypes.h" + +// Class to convert a HANDLE to a FILE *. The FILE * is closed when the +// object goes out of scope +class HandleToFile { + public: + HandleToFile() { + file_ = NULL; + }; + + // Note: c_file_handle_ does not need to be closed because fclose does it. + ~HandleToFile() { + if (file_) { + fflush(file_); + fclose(file_); + } + }; + + // Translates a HANDLE (handle) to a FILE * opened with the mode "mode". + // The return value is the FILE * or NULL if there is an error. + FILE* Translate(HANDLE handle, const char *mode) { + if (file_) { + return NULL; + } + + HANDLE new_handle; + BOOL result = ::DuplicateHandle(::GetCurrentProcess(), + handle, + ::GetCurrentProcess(), + &new_handle, + 0, // Don't ask for a specific + // desired access. + FALSE, // Not inheritable. + DUPLICATE_SAME_ACCESS); + + if (!result) { + return NULL; + } + + int c_file_handle = _open_osfhandle(reinterpret_cast(new_handle), + 0); // No flags + if (-1 == c_file_handle) { + return NULL; + } + + file_ = _fdopen(c_file_handle, mode); + return file_; + }; + private: + // the FILE* returned. We need to closed it at the end. + FILE* file_; + + DISALLOW_COPY_AND_ASSIGN(HandleToFile); +}; + +#endif // SANDBOX_SANDBOX_POC_POCDLL_UTILS_H__ diff --git a/sandbox/win/sandbox_poc/resource.h b/sandbox/win/sandbox_poc/resource.h new file mode 100644 index 0000000..87ff920 --- /dev/null +++ b/sandbox/win/sandbox_poc/resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by sandbox.rc +// +#define IDI_SANDBOX 107 +#define IDR_MENU_MAIN_UI 129 +#define IDD_LAUNCH_DLL 130 +#define IDC_RADIO_POCDLL 1000 +#define IDC_RADIO_CUSTOM_DLL 1001 +#define IDC_DLL_NAME 1002 +#define IDC_ENTRY_POINT 1003 +#define IDC_LOG_FILE 1004 +#define IDC_BROWSE_DLL 1005 +#define IDC_BROWSE_LOG 1006 +#define ID_FILE_EXIT 32771 +#define ID_COMMANDS_LAUNCHDLL 32772 +#define ID_COMMANDS_SPAWNTARGET 32773 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_COMMAND_VALUE 32774 +#define _APS_NEXT_CONTROL_VALUE 1007 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/sandbox/win/sandbox_poc/sandbox.cc b/sandbox/win/sandbox_poc/sandbox.cc new file mode 100644 index 0000000..4dc0882 --- /dev/null +++ b/sandbox/win/sandbox_poc/sandbox.cc @@ -0,0 +1,182 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include "sandbox/sandbox_poc/sandbox.h" +#include "base/logging.h" +#include "sandbox/sandbox_poc/main_ui_window.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_factory.h" + +// Prototype allowed for functions to be called in the POC +typedef void(__cdecl *lpfnInit)(HANDLE); + +bool ParseCommandLine(wchar_t * command_line, + std::string * dll_name, + std::string * entry_point, + std::wstring * log_file) { + DCHECK(dll_name); + DCHECK(entry_point); + DCHECK(log_file); + if (!dll_name || !entry_point || !log_file) + return false; + + LPWSTR *arg_list; + int arg_count; + + // We expect the command line to contain: EntryPointName "DLLPath" "LogPath" + // NOTE: Double quotes are required, even if long path name not used + // NOTE: LogPath can be blank, but still requires the double quotes + arg_list = CommandLineToArgvW(command_line, &arg_count); + if (NULL == arg_list || arg_count < 4) { + return false; + } + + std::wstring entry_point_wide = arg_list[1]; + std::wstring dll_name_wide = arg_list[2]; + *entry_point = std::string(entry_point_wide.begin(), entry_point_wide.end()); + *dll_name = std::string(dll_name_wide.begin(), dll_name_wide.end()); + *log_file = arg_list[3]; + + // Free memory allocated for CommandLineToArgvW arguments. + LocalFree(arg_list); + + return true; +} + +int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, wchar_t* command_line, + int show_command) { + UNREFERENCED_PARAMETER(command_line); + + sandbox::BrokerServices* broker_service = + sandbox::SandboxFactory::GetBrokerServices(); + sandbox::ResultCode result; + + // This application starts as the broker; an application with a UI that + // spawns an instance of itself (called a 'target') inside the sandbox. + // Before spawning a hidden instance of itself, the application will have + // asked the user which DLL the spawned instance should load and passes + // that as command line argument to the spawned instance. + // + // We check here to see if we can retrieve a pointer to the BrokerServices, + // which is not possible if we are running inside the sandbox under a + // restricted token so it also tells us which mode we are in. If we can + // retrieve the pointer, then we are the broker, otherwise we are the target + // that the broker launched. + if (NULL != broker_service) { + // Yes, we are the broker so we need to initialize and show the UI + if (0 != (result = broker_service->Init())) { + ::MessageBox(NULL, L"Failed to initialize the BrokerServices object", + L"Error during initialization", MB_ICONERROR); + return 1; + } + + wchar_t exe_name[MAX_PATH]; + if (0 == GetModuleFileName(NULL, exe_name, MAX_PATH - 1)) { + ::MessageBox(NULL, L"Failed to get name of current EXE", + L"Error during initialization", MB_ICONERROR); + return 1; + } + + // The CreateMainWindowAndLoop() call will not return until the user closes + // the application window (or selects File\Exit). + MainUIWindow window; + window.CreateMainWindowAndLoop(instance, + exe_name, + show_command, + broker_service); + + + // Cannot exit until we have cleaned up after all the targets we have + // created + broker_service->WaitForAllTargets(); + } else { + // This is an instance that has been spawned inside the sandbox by the + // broker, so we need to parse the command line to figure out which DLL to + // load and what entry point to call + sandbox::TargetServices* target_service + = sandbox::SandboxFactory::GetTargetServices(); + + if (NULL == target_service) { + // TODO(finnur): write the failure to the log file + // We cannot display messageboxes inside the sandbox unless access to + // the desktop handle has been granted to us, and we don't have a + // console window to write to. Therefore we need to have the broker + // grant us access to a handle to a logfile and write the error that + // occurred into the log before continuing + return -1; + } + + // Debugging the spawned application can be tricky, because DebugBreak() + // and _asm int 3 cause the app to terminate (due to a flag in the job + // object), MessageBoxes() will not be displayed unless we have been granted + // that privilege and the target finishes its business so quickly we cannot + // attach to it quickly enough. Therefore, you can uncomment the + // following line and attach (w. msdev or windbg) as the target is sleeping + + // Sleep(10000); + + if (sandbox::SBOX_ALL_OK != (result = target_service->Init())) { + // TODO(finnur): write the initialization error to the log file + return -2; + } + + // Parse the command line to find out what we need to call + std::string dll_name, entry_point; + std::wstring log_file; + if (!ParseCommandLine(GetCommandLineW(), + &dll_name, + &entry_point, + &log_file)) { + // TODO(finnur): write the failure to the log file + return -3; + } + + // Open the pipe to transfert the log output + HANDLE pipe = ::CreateFile(log_file.c_str(), + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, // Default security attributes. + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); // No template + + if (INVALID_HANDLE_VALUE == pipe) { + return -4; + } + + // We now know what we should load, so load it + HMODULE dll_module = ::LoadLibraryA(dll_name.c_str()); + if (dll_module == NULL) { + // TODO(finnur): write the failure to the log file + return -5; + } + + // Initialization is finished, so we can enter lock-down mode + target_service->LowerToken(); + + lpfnInit init_function = + (lpfnInit) ::GetProcAddress(dll_module, entry_point.c_str()); + + if (!init_function) { + // TODO(finnur): write the failure to the log file + ::FreeLibrary(dll_module); + CloseHandle(pipe); + return -6; + } + + // Transfer control to the entry point in the DLL requested + init_function(pipe); + + CloseHandle(pipe); + Sleep(1000); // Give a change to the debug output to arrive before the + // end of the process + + ::FreeLibrary(dll_module); + } + + return 0; +} diff --git a/sandbox/win/sandbox_poc/sandbox.h b/sandbox/win/sandbox_poc/sandbox.h new file mode 100644 index 0000000..15531ad --- /dev/null +++ b/sandbox/win/sandbox_poc/sandbox.h @@ -0,0 +1,10 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SANDBOX_POC_SANDBOX_H__ +#define SANDBOX_SANDBOX_POC_SANDBOX_H__ + +#include "sandbox/sandbox_poc/resource.h" + +#endif // SANDBOX_SANDBOX_POC_SANDBOX_H__ diff --git a/sandbox/win/sandbox_poc/sandbox.ico b/sandbox/win/sandbox_poc/sandbox.ico new file mode 100644 index 0000000..916fa12 Binary files /dev/null and b/sandbox/win/sandbox_poc/sandbox.ico differ diff --git a/sandbox/win/sandbox_poc/sandbox.rc b/sandbox/win/sandbox_poc/sandbox.rc new file mode 100644 index 0000000..978c96f --- /dev/null +++ b/sandbox/win/sandbox_poc/sandbox.rc @@ -0,0 +1,136 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU_MAIN_UI MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "E&xit", ID_FILE_EXIT + END + POPUP "&Commands" + BEGIN + MENUITEM "&Spawn target", ID_COMMANDS_SPAWNTARGET + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_LAUNCH_DLL DIALOGEX 0, 0, 269, 118 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "BrokerUI: Load an Attack DLL" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Call now",IDOK,212,70,50,14 + PUSHBUTTON "Cancel",IDCANCEL,212,95,50,14 + EDITTEXT IDC_DLL_NAME,7,43,200,13,ES_AUTOHSCROLL + LTEXT "DLL to load in target:",IDC_STATIC,7,33,168,8 + LTEXT "Function to call:",IDC_STATIC,7,61,139,8 + EDITTEXT IDC_ENTRY_POINT,7,71,200,13,ES_AUTOHSCROLL + EDITTEXT IDC_LOG_FILE,7,17,200,13,ES_AUTOHSCROLL + LTEXT "File for Target logging (optional):",IDC_STATIC,7,7,139,8 + PUSHBUTTON "Browse...",IDC_BROWSE_DLL,212,42,50,14 + PUSHBUTTON "Browse...",IDC_BROWSE_LOG,212,16,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_LAUNCH_DLL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 262 + TOPMARGIN, 7 + BOTTOMMARGIN, 111 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SANDBOX ICON "sandbox.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/sandbox/win/sandbox_poc/sandbox_poc.vcproj b/sandbox/win/sandbox_poc/sandbox_poc.vcproj new file mode 100644 index 0000000..5fde1cd --- /dev/null +++ b/sandbox/win/sandbox_poc/sandbox_poc.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/win/sandbox_standalone.sln b/sandbox/win/sandbox_standalone.sln new file mode 100644 index 0000000..529d20e --- /dev/null +++ b/sandbox/win/sandbox_standalone.sln @@ -0,0 +1,127 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox", "src\sandbox.vcproj", "{881F6A97-D539-4C48-B401-DF04385B2343}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sbox_unittests", "tests\unit_tests\sbox_unittests.vcproj", "{883553BE-2A9D-418C-A121-61FE1DFBC562}" + ProjectSection(ProjectDependencies) = postProject + {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} + {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} + {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sbox_validation_tests", "tests\validation_tests\sbox_validation_tests.vcproj", "{B9CC7B0D-145A-49C2-B887-84E43CFA0F27}" + ProjectSection(ProjectDependencies) = postProject + {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} + {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} + {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dependencies", "dependencies", "{BCE54389-D18D-48B9-977E-9D1998200F63}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debug_message", "..\base\debug_message.vcproj", "{F0F92189-193A-6607-C2BB-0F98BBD19ADF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{7F36EE20-5016-4051-B0D7-42824CDA0291}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "proof_of_concept", "proof_of_concept", "{B607BE7B-3555-422C-A40B-28E73C0B5E24}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox_poc", "sandbox_poc\sandbox_poc.vcproj", "{CF757839-F2A1-417C-8F25-DCAE480020F1}" + ProjectSection(ProjectDependencies) = postProject + {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} + {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} + {AE5BFB87-850E-4454-B01D-58E7D8BAC224} = {AE5BFB87-850E-4454-B01D-58E7D8BAC224} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pocdll", "sandbox_poc\pocdll\pocdll.vcproj", "{AE5BFB87-850E-4454-B01D-58E7D8BAC224}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "finder", "tools\finder\finder.vcproj", "{ACDC2E06-0366-41A4-A646-C37E130A605D}" + ProjectSection(ProjectDependencies) = postProject + {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} + {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "launcher", "tools\launcher\launcher.vcproj", "{386FA217-FBC2-4461-882D-CDAD221ED800}" + ProjectSection(ProjectDependencies) = postProject + {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} + {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sbox_integration_tests", "tests\integration_tests\sbox_integration_tests.vcproj", "{542D4B3B-98D4-4233-B68D-0103891508C6}" + ProjectSection(ProjectDependencies) = postProject + {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165} + {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343} + {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base", "..\base\base.vcproj", "{1832A374-8A74-4F9E-B536-69A699B3E165}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest", "..\testing\gtest.vcproj", "{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {881F6A97-D539-4C48-B401-DF04385B2343}.Debug|Win32.ActiveCfg = Debug|Win32 + {881F6A97-D539-4C48-B401-DF04385B2343}.Debug|Win32.Build.0 = Debug|Win32 + {881F6A97-D539-4C48-B401-DF04385B2343}.Release|Win32.ActiveCfg = Release|Win32 + {881F6A97-D539-4C48-B401-DF04385B2343}.Release|Win32.Build.0 = Release|Win32 + {883553BE-2A9D-418C-A121-61FE1DFBC562}.Debug|Win32.ActiveCfg = Debug|Win32 + {883553BE-2A9D-418C-A121-61FE1DFBC562}.Debug|Win32.Build.0 = Debug|Win32 + {883553BE-2A9D-418C-A121-61FE1DFBC562}.Release|Win32.ActiveCfg = Release|Win32 + {883553BE-2A9D-418C-A121-61FE1DFBC562}.Release|Win32.Build.0 = Release|Win32 + {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Debug|Win32.ActiveCfg = Debug|Win32 + {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Debug|Win32.Build.0 = Debug|Win32 + {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Release|Win32.ActiveCfg = Release|Win32 + {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Release|Win32.Build.0 = Release|Win32 + {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Debug|Win32.ActiveCfg = Debug|Win32 + {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Debug|Win32.Build.0 = Debug|Win32 + {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Release|Win32.ActiveCfg = Release|Win32 + {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Release|Win32.Build.0 = Release|Win32 + {CF757839-F2A1-417C-8F25-DCAE480020F1}.Debug|Win32.ActiveCfg = Debug|Win32 + {CF757839-F2A1-417C-8F25-DCAE480020F1}.Debug|Win32.Build.0 = Debug|Win32 + {CF757839-F2A1-417C-8F25-DCAE480020F1}.Release|Win32.ActiveCfg = Release|Win32 + {CF757839-F2A1-417C-8F25-DCAE480020F1}.Release|Win32.Build.0 = Release|Win32 + {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Debug|Win32.ActiveCfg = Debug|Win32 + {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Debug|Win32.Build.0 = Debug|Win32 + {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Release|Win32.ActiveCfg = Release|Win32 + {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Release|Win32.Build.0 = Release|Win32 + {ACDC2E06-0366-41A4-A646-C37E130A605D}.Debug|Win32.ActiveCfg = Debug|Win32 + {ACDC2E06-0366-41A4-A646-C37E130A605D}.Debug|Win32.Build.0 = Debug|Win32 + {ACDC2E06-0366-41A4-A646-C37E130A605D}.Release|Win32.ActiveCfg = Release|Win32 + {ACDC2E06-0366-41A4-A646-C37E130A605D}.Release|Win32.Build.0 = Release|Win32 + {386FA217-FBC2-4461-882D-CDAD221ED800}.Debug|Win32.ActiveCfg = Debug|Win32 + {386FA217-FBC2-4461-882D-CDAD221ED800}.Debug|Win32.Build.0 = Debug|Win32 + {386FA217-FBC2-4461-882D-CDAD221ED800}.Release|Win32.ActiveCfg = Release|Win32 + {386FA217-FBC2-4461-882D-CDAD221ED800}.Release|Win32.Build.0 = Release|Win32 + {542D4B3B-98D4-4233-B68D-0103891508C6}.Debug|Win32.ActiveCfg = Debug|Win32 + {542D4B3B-98D4-4233-B68D-0103891508C6}.Debug|Win32.Build.0 = Debug|Win32 + {542D4B3B-98D4-4233-B68D-0103891508C6}.Release|Win32.ActiveCfg = Release|Win32 + {542D4B3B-98D4-4233-B68D-0103891508C6}.Release|Win32.Build.0 = Release|Win32 + {1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.ActiveCfg = Debug|Win32 + {1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.Build.0 = Debug|Win32 + {1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.ActiveCfg = Release|Win32 + {1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.Build.0 = Release|Win32 + {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.ActiveCfg = Debug|Win32 + {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.Build.0 = Debug|Win32 + {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.ActiveCfg = Release|Win32 + {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {883553BE-2A9D-418C-A121-61FE1DFBC562} = {F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED} + {B9CC7B0D-145A-49C2-B887-84E43CFA0F27} = {F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED} + {542D4B3B-98D4-4233-B68D-0103891508C6} = {F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED} + {F0F92189-193A-6607-C2BB-0F98BBD19ADF} = {BCE54389-D18D-48B9-977E-9D1998200F63} + {1832A374-8A74-4F9E-B536-69A699B3E165} = {BCE54389-D18D-48B9-977E-9D1998200F63} + {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BCE54389-D18D-48B9-977E-9D1998200F63} + {ACDC2E06-0366-41A4-A646-C37E130A605D} = {7F36EE20-5016-4051-B0D7-42824CDA0291} + {386FA217-FBC2-4461-882D-CDAD221ED800} = {7F36EE20-5016-4051-B0D7-42824CDA0291} + {CF757839-F2A1-417C-8F25-DCAE480020F1} = {B607BE7B-3555-422C-A40B-28E73C0B5E24} + {AE5BFB87-850E-4454-B01D-58E7D8BAC224} = {B607BE7B-3555-422C-A40B-28E73C0B5E24} + EndGlobalSection +EndGlobal diff --git a/sandbox/win/sandbox_win.gypi b/sandbox/win/sandbox_win.gypi new file mode 100644 index 0000000..7952bfe --- /dev/null +++ b/sandbox/win/sandbox_win.gypi @@ -0,0 +1,342 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'target_defaults': { + 'variables': { + 'sandbox_windows_target': 0, + }, + 'target_conditions': [ + ['sandbox_windows_target==1', { + # Files that are shared between the 32-bit and the 64-bit versions + # of the Windows sandbox library. + 'sources': [ + 'src/acl.cc', + 'src/acl.h', + 'src/broker_services.cc', + 'src/broker_services.h', + 'src/crosscall_client.h', + 'src/crosscall_params.h', + 'src/crosscall_server.cc', + 'src/crosscall_server.h', + 'src/dep.cc', + 'src/dep.h', + 'src/eat_resolver.cc', + 'src/eat_resolver.h', + 'src/filesystem_dispatcher.cc', + 'src/filesystem_dispatcher.h', + 'src/filesystem_interception.cc', + 'src/filesystem_interception.h', + 'src/filesystem_policy.cc', + 'src/filesystem_policy.h', + 'src/handle_closer.cc', + 'src/handle_closer.h', + 'src/handle_closer_agent.cc', + 'src/handle_closer_agent.h', + 'src/handle_dispatcher.cc', + 'src/handle_dispatcher.h', + 'src/handle_interception.cc', + 'src/handle_interception.h', + 'src/handle_policy.cc', + 'src/handle_policy.h', + 'src/handle_table.cc', + 'src/handle_table.h', + 'src/interception.cc', + 'src/interception.h', + 'src/interception_agent.cc', + 'src/interception_agent.h', + 'src/interception_internal.h', + 'src/interceptors.h', + 'src/internal_types.h', + 'src/ipc_tags.h', + 'src/job.cc', + 'src/job.h', + 'src/named_pipe_dispatcher.cc', + 'src/named_pipe_dispatcher.h', + 'src/named_pipe_interception.cc', + 'src/named_pipe_interception.h', + 'src/named_pipe_policy.cc', + 'src/named_pipe_policy.h', + 'src/nt_internals.h', + 'src/policy_broker.cc', + 'src/policy_broker.h', + 'src/policy_engine_opcodes.cc', + 'src/policy_engine_opcodes.h', + 'src/policy_engine_params.h', + 'src/policy_engine_processor.cc', + 'src/policy_engine_processor.h', + 'src/policy_low_level.cc', + 'src/policy_low_level.h', + 'src/policy_params.h', + 'src/policy_target.cc', + 'src/policy_target.h', + 'src/process_thread_dispatcher.cc', + 'src/process_thread_dispatcher.h', + 'src/process_thread_interception.cc', + 'src/process_thread_interception.h', + 'src/process_thread_policy.cc', + 'src/process_thread_policy.h', + 'src/registry_dispatcher.cc', + 'src/registry_dispatcher.h', + 'src/registry_interception.cc', + 'src/registry_interception.h', + 'src/registry_policy.cc', + 'src/registry_policy.h', + 'src/resolver.cc', + 'src/resolver.h', + 'src/restricted_token_utils.cc', + 'src/restricted_token_utils.h', + 'src/restricted_token.cc', + 'src/restricted_token.h', + 'src/sandbox_factory.h', + 'src/sandbox_nt_types.h', + 'src/sandbox_nt_util.cc', + 'src/sandbox_nt_util.h', + 'src/sandbox_policy_base.cc', + 'src/sandbox_policy_base.h', + 'src/sandbox_policy.h', + 'src/sandbox_types.h', + 'src/sandbox_utils.cc', + 'src/sandbox_utils.h', + 'src/sandbox.cc', + 'src/sandbox.h', + 'src/security_level.h', + 'src/service_resolver.cc', + 'src/service_resolver.h', + 'src/shared_handles.cc', + 'src/shared_handles.h', + 'src/sharedmem_ipc_client.cc', + 'src/sharedmem_ipc_client.h', + 'src/sharedmem_ipc_server.cc', + 'src/sharedmem_ipc_server.h', + 'src/sid.cc', + 'src/sid.h', + 'src/sync_dispatcher.cc', + 'src/sync_dispatcher.h', + 'src/sync_interception.cc', + 'src/sync_interception.h', + 'src/sync_policy.cc', + 'src/sync_policy.h', + 'src/target_interceptions.cc', + 'src/target_interceptions.h', + 'src/target_process.cc', + 'src/target_process.h', + 'src/target_services.cc', + 'src/target_services.h', + 'src/win_utils.cc', + 'src/win_utils.h', + 'src/win2k_threadpool.cc', + 'src/win2k_threadpool.h', + 'src/window.cc', + 'src/window.h', + ], + }], + ], + }, + 'targets': [ + { + 'target_name': 'sandbox', + 'type': 'static_library', + 'variables': { + 'sandbox_windows_target': 1, + }, + 'dependencies': [ + '../../testing/gtest.gyp:gtest', + '../../base/base.gyp:base', + '../../base/base.gyp:base_static', + ], + 'export_dependent_settings': [ + '../../base/base.gyp:base', + ], + 'sources': [ + # Files that are used by the 32-bit version of Windows sandbox only. + 'src/resolver_32.cc', + 'src/service_resolver_32.cc', + 'src/sidestep_resolver.cc', + 'src/sidestep_resolver.h', + 'src/sidestep\ia32_modrm_map.cpp', + 'src/sidestep\ia32_opcode_map.cpp', + 'src/sidestep\mini_disassembler_types.h', + 'src/sidestep\mini_disassembler.cpp', + 'src/sidestep\mini_disassembler.h', + 'src/sidestep\preamble_patcher_with_stub.cpp', + 'src/sidestep\preamble_patcher.h', + 'src/Wow64.cc', + 'src/Wow64.h', + ], + 'include_dirs': [ + '../..', + ], + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)', + 'files': [ + 'wow_helper/wow_helper.exe', + 'wow_helper/wow_helper.pdb', + ], + }, + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'src', + '../..', + ], + }, + }, + { + 'target_name': 'sandbox_win64', + 'type': 'static_library', + 'variables': { + 'sandbox_windows_target': 1, + }, + 'dependencies': [ + '../../testing/gtest.gyp:gtest', + '../../base/base.gyp:base_nacl_win64', + '../../base/base.gyp:base_static_win64', + ], + 'configurations': { + 'Common_Base': { + 'msvs_target_platform': 'x64', + }, + }, + 'sources': [ + # Files that are used by the 64-bit version of Windows sandbox only. + 'src/interceptors_64.cc', + 'src/interceptors_64.h', + 'src/resolver_64.cc', + 'src/service_resolver_64.cc', + 'src/Wow64_64.cc', + ], + 'include_dirs': [ + '../..', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'src', + '../..', + ], + }, + 'defines': [ + '<@(nacl_win64_defines)', + ] + }, + { + 'target_name': 'sbox_integration_tests', + 'type': 'executable', + 'dependencies': [ + 'sandbox', + '../../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'tests/common/controller.cc', + 'tests/common/controller.h', + 'tests/common/test_utils.cc', + 'tests/common/test_utils.h', + 'tests/integration_tests/integration_tests.cc', + 'src/dep_test.cc', + 'src/file_policy_test.cc', + 'src/handle_policy_test.cc', + 'tests/integration_tests/integration_tests_test.cc', + 'src/handle_closer_test.cc', + 'src/integrity_level_test.cc', + 'src/ipc_ping_test.cc', + 'src/named_pipe_policy_test.cc', + 'src/policy_target_test.cc', + 'src/process_policy_test.cc', + 'src/registry_policy_test.cc', + 'src/sync_policy_test.cc', + 'src/unload_dll_test.cc', + ], + }, + { + 'target_name': 'sbox_validation_tests', + 'type': 'executable', + 'dependencies': [ + 'sandbox', + '../../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'tests/common/controller.cc', + 'tests/common/controller.h', + 'tests/validation_tests/unit_tests.cc', + 'tests/validation_tests/commands.cc', + 'tests/validation_tests/commands.h', + 'tests/validation_tests/suite.cc', + ], + }, + { + 'target_name': 'sbox_unittests', + 'type': 'executable', + 'dependencies': [ + 'sandbox', + '../../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'tests/common/test_utils.cc', + 'tests/common/test_utils.h', + 'tests/unit_tests/unit_tests.cc', + 'src/interception_unittest.cc', + 'src/service_resolver_unittest.cc', + 'src/restricted_token_unittest.cc', + 'src/job_unittest.cc', + 'src/sid_unittest.cc', + 'src/policy_engine_unittest.cc', + 'src/policy_low_level_unittest.cc', + 'src/policy_opcodes_unittest.cc', + 'src/ipc_unittest.cc', + 'src/threadpool_unittest.cc', + 'src/win_utils_unittest.cc', + ], + }, + { + 'target_name': 'sandbox_poc', + 'type': 'executable', + 'dependencies': [ + 'sandbox', + 'pocdll', + ], + 'sources': [ + 'sandbox_poc/main_ui_window.cc', + 'sandbox_poc/main_ui_window.h', + 'sandbox_poc/resource.h', + 'sandbox_poc/sandbox.cc', + 'sandbox_poc/sandbox.h', + 'sandbox_poc/sandbox.ico', + 'sandbox_poc/sandbox.rc', + ], + 'link_settings': { + 'libraries': [ + '-lcomctl32.lib', + ], + }, + 'msvs_settings': { + 'VCLinkerTool': { + 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS + }, + }, + }, + { + 'target_name': 'pocdll', + 'type': 'shared_library', + 'sources': [ + 'sandbox_poc/pocdll/exports.h', + 'sandbox_poc/pocdll/fs.cc', + 'sandbox_poc/pocdll/handles.cc', + 'sandbox_poc/pocdll/invasive.cc', + 'sandbox_poc/pocdll/network.cc', + 'sandbox_poc/pocdll/pocdll.cc', + 'sandbox_poc/pocdll/processes_and_threads.cc', + 'sandbox_poc/pocdll/registry.cc', + 'sandbox_poc/pocdll/spyware.cc', + 'sandbox_poc/pocdll/utils.h', + ], + 'defines': [ + 'POCDLL_EXPORTS', + ], + 'include_dirs': [ + '../..', + ], + }, + ], +} diff --git a/sandbox/win/src/Wow64.cc b/sandbox/win/src/Wow64.cc new file mode 100644 index 0000000..5098647 --- /dev/null +++ b/sandbox/win/src/Wow64.cc @@ -0,0 +1,219 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/wow64.h" + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/win/scoped_process_information.h" +#include "base/win/windows_version.h" +#include "sandbox/src/target_process.h" + +namespace { + +// Holds the information needed for the interception of NtMapViewOfSection on +// 64 bits. +// Warning: do not modify this definition without changing also the code on the +// 64 bit helper process. +struct PatchInfo32 { + HANDLE dll_load; // Event to signal the broker. + ULONG pad1; + HANDLE continue_load; // Event to wait for the broker. + ULONG pad2; + HANDLE section; // First argument of the call. + ULONG pad3; + void* orig_MapViewOfSection; + ULONG original_high; + void* signal_and_wait; + ULONG pad4; + void* patch_location; + ULONG patch_high; +}; + +// Size of the 64 bit service entry. +const SIZE_T kServiceEntry64Size = 0x10; + +// Removes the interception of ntdll64. +bool Restore64Code(HANDLE child, PatchInfo32* patch_info) { + PatchInfo32 local_patch_info; + SIZE_T actual; + if (!::ReadProcessMemory(child, patch_info, &local_patch_info, + sizeof(local_patch_info), &actual)) + return false; + if (sizeof(local_patch_info) != actual) + return false; + + if (local_patch_info.original_high) + return false; + if (local_patch_info.patch_high) + return false; + + char buffer[kServiceEntry64Size]; + + if (!::ReadProcessMemory(child, local_patch_info.orig_MapViewOfSection, + &buffer, kServiceEntry64Size, &actual)) + return false; + if (kServiceEntry64Size != actual) + return false; + + if (!::WriteProcessMemory(child, local_patch_info.patch_location, &buffer, + kServiceEntry64Size, &actual)) + return false; + if (kServiceEntry64Size != actual) + return false; + return true; +} + +typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64); + +} // namespace + +namespace sandbox { + +Wow64::~Wow64() { + if (dll_load_) + ::CloseHandle(dll_load_); + + if (continue_load_) + ::CloseHandle(continue_load_); +} + +// The basic idea is to allocate one page of memory on the child, and initialize +// the first part of it with our version of PatchInfo32. Then launch the helper +// process passing it that address on the child. The helper process will patch +// the 64 bit version of NtMapViewOfFile, and the interception will signal the +// first event on the buffer. We'll be waiting on that event and after the 32 +// bit version of ntdll is loaded, we'll remove the interception and return to +// our caller. +bool Wow64::WaitForNtdll() { + if (base::win::OSInfo::GetInstance()->wow64_status() != + base::win::OSInfo::WOW64_ENABLED) + return true; + + const size_t page_size = 4096; + + // Create some default manual reset un-named events, not signaled. + dll_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL); + continue_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL); + HANDLE current_process = ::GetCurrentProcess(); + HANDLE remote_load, remote_continue; + DWORD access = EVENT_MODIFY_STATE | SYNCHRONIZE; + if (!::DuplicateHandle(current_process, dll_load_, child_->Process(), + &remote_load, access, FALSE, 0)) + return false; + if (!::DuplicateHandle(current_process, continue_load_, child_->Process(), + &remote_continue, access, FALSE, 0)) + return false; + + void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size, + MEM_COMMIT, PAGE_EXECUTE_READWRITE); + DCHECK(buffer); + if (!buffer) + return false; + + PatchInfo32* patch_info = reinterpret_cast(buffer); + PatchInfo32 local_patch_info = {0}; + local_patch_info.dll_load = remote_load; + local_patch_info.continue_load = remote_continue; + SIZE_T written; + if (!::WriteProcessMemory(child_->Process(), patch_info, &local_patch_info, + offsetof(PatchInfo32, section), &written)) + return false; + if (offsetof(PatchInfo32, section) != written) + return false; + + if (!RunWowHelper(buffer)) + return false; + + // The child is intercepted on 64 bit, go on and wait for our event. + if (!DllMapped()) + return false; + + // The 32 bit version is available, cleanup the child. + return Restore64Code(child_->Process(), patch_info); +} + +bool Wow64::RunWowHelper(void* buffer) { + COMPILE_ASSERT(sizeof(buffer) <= sizeof(DWORD), unsupported_64_bits); + + // Get the path to the helper (beside the exe). + wchar_t prog_name[MAX_PATH]; + GetModuleFileNameW(NULL, prog_name, MAX_PATH); + std::wstring path(prog_name); + size_t name_pos = path.find_last_of(L"\\"); + if (std::wstring::npos == name_pos) + return false; + path.resize(name_pos + 1); + + std::wstringstream command; + command << std::hex << std::showbase << L"\"" << path << + L"wow_helper.exe\" " << child_->ProcessId() << " " << + bit_cast(buffer); + + scoped_ptr_malloc writable_command(_wcsdup(command.str().c_str())); + + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + base::win::ScopedProcessInformation process_info; + if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL, + NULL, &startup_info, process_info.Receive())) + return false; + + DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE); + + DWORD code; + bool ok = + ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false; + + if (WAIT_TIMEOUT == reason) + return false; + + return ok && (0 == code); +} + +// First we must wake up the child, then wait for dll loads on the child until +// the one we care is loaded; at that point we must suspend the child again. +bool Wow64::DllMapped() { + if (1 != ::ResumeThread(child_->MainThread())) { + NOTREACHED(); + return false; + } + + for (;;) { + DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE); + if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason) + return false; + + if (!::ResetEvent(dll_load_)) + return false; + + bool found = NtdllPresent(); + if (found) { + if (::SuspendThread(child_->MainThread())) + return false; + } + + if (!::SetEvent(continue_load_)) + return false; + + if (found) + return true; + } +} + +bool Wow64::NtdllPresent() { + const size_t kBufferSize = 512; + char buffer[kBufferSize]; + SIZE_T read; + if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize, + &read)) + return false; + if (kBufferSize != read) + return false; + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/Wow64.h b/sandbox/win/src/Wow64.h new file mode 100644 index 0000000..472297e --- /dev/null +++ b/sandbox/win/src/Wow64.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_WOW64_H__ +#define SANDBOX_SRC_WOW64_H__ + +#include + +#include "base/basictypes.h" +#include "sandbox/src/sandbox_types.h" + +namespace sandbox { + +class TargetProcess; + +// This class wraps the code needed to interact with the Windows On Windows +// subsystem on 64 bit OSes, from the point of view of interceptions. +class Wow64 { + public: + Wow64(TargetProcess* child, HMODULE ntdll) + : child_(child), ntdll_(ntdll), dll_load_(NULL), continue_load_(NULL) {} + ~Wow64(); + + // Waits for the 32 bit DLL to get loaded on the child process. This function + // will return immediately if not running under WOW, or launch the helper + // process and wait until ntdll is ready. + bool WaitForNtdll(); + + private: + // Runs the WOW helper process, passing the address of a buffer allocated on + // the child (one page). + bool RunWowHelper(void* buffer); + + // This method receives "notifications" whenever a DLL is mapped on the child. + bool DllMapped(); + + // Returns true if ntdll.dll is mapped on the child. + bool NtdllPresent(); + + TargetProcess* child_; // Child process. + HMODULE ntdll_; // ntdll on the parent. + HANDLE dll_load_; // Event that is signaled on dll load. + HANDLE continue_load_; // Event to signal to continue execution on the child. + DISALLOW_IMPLICIT_CONSTRUCTORS(Wow64); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_WOW64_H__ diff --git a/sandbox/win/src/Wow64_64.cc b/sandbox/win/src/Wow64_64.cc new file mode 100644 index 0000000..5218077 --- /dev/null +++ b/sandbox/win/src/Wow64_64.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Wow64 implementation for native 64-bit Windows (in other words, never WOW). + +#include "sandbox/src/wow64.h" + +namespace sandbox { + +Wow64::~Wow64() { +} + +bool Wow64::WaitForNtdll() { + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/acl.cc b/sandbox/win/src/acl.cc new file mode 100644 index 0000000..4869bb0 --- /dev/null +++ b/sandbox/win/src/acl.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/acl.h" + +#include +#include + +#include "base/logging.h" + +namespace sandbox { + +bool GetDefaultDacl(HANDLE token, + scoped_ptr_malloc* default_dacl) { + if (token == NULL) + return false; + + DCHECK(default_dacl != NULL); + + unsigned long length = 0; + ::GetTokenInformation(token, TokenDefaultDacl, NULL, 0, &length); + if (length == 0) { + NOTREACHED(); + return false; + } + + TOKEN_DEFAULT_DACL* acl = + reinterpret_cast(malloc(length)); + default_dacl->reset(acl); + + if (!::GetTokenInformation(token, TokenDefaultDacl, default_dacl->get(), + length, &length)) + return false; + + return true; +} + +bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MASK access, + ACL** new_dacl) { + EXPLICIT_ACCESS new_access = {0}; + new_access.grfAccessMode = GRANT_ACCESS; + new_access.grfAccessPermissions = access; + new_access.grfInheritance = NO_INHERITANCE; + + new_access.Trustee.pMultipleTrustee = NULL; + new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID; + new_access.Trustee.ptstrName = reinterpret_cast( + const_cast(sid.GetPSID())); + + if (ERROR_SUCCESS != ::SetEntriesInAcl(1, &new_access, old_dacl, new_dacl)) + return false; + + return true; +} + +bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access) { + if (token == NULL) + return false; + + scoped_ptr_malloc default_dacl; + if (!GetDefaultDacl(token, &default_dacl)) + return false; + + ACL* new_dacl = NULL; + if (!AddSidToDacl(sid, default_dacl->DefaultDacl, access, &new_dacl)) + return false; + + TOKEN_DEFAULT_DACL new_token_dacl = {0}; + new_token_dacl.DefaultDacl = new_dacl; + + BOOL ret = ::SetTokenInformation(token, TokenDefaultDacl, &new_token_dacl, + sizeof(new_token_dacl)); + ::LocalFree(new_dacl); + return (TRUE == ret); +} + +bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access) { + DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; + TOKEN_USER* token_user = reinterpret_cast(malloc(size)); + + scoped_ptr_malloc token_user_ptr(token_user); + + if (!::GetTokenInformation(token, TokenUser, token_user, size, &size)) + return false; + + return AddSidToDefaultDacl(token, + reinterpret_cast(token_user->User.Sid), + access); +} + +bool AddKnownSidToKernelObject(HANDLE object, const Sid& sid, + ACCESS_MASK access) { + PSECURITY_DESCRIPTOR descriptor = NULL; + PACL old_dacl = NULL; + PACL new_dacl = NULL; + + if (ERROR_SUCCESS != ::GetSecurityInfo(object, SE_KERNEL_OBJECT, + DACL_SECURITY_INFORMATION, NULL, NULL, + &old_dacl, NULL, &descriptor)) + return false; + + if (!AddSidToDacl(sid.GetPSID(), old_dacl, access, &new_dacl)) { + ::LocalFree(descriptor); + return false; + } + + DWORD result = ::SetSecurityInfo(object, SE_KERNEL_OBJECT, + DACL_SECURITY_INFORMATION, NULL, NULL, + new_dacl, NULL); + + ::LocalFree(new_dacl); + ::LocalFree(descriptor); + + if (ERROR_SUCCESS != result) + return false; + + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/acl.h b/sandbox/win/src/acl.h new file mode 100644 index 0000000..d452011 --- /dev/null +++ b/sandbox/win/src/acl.h @@ -0,0 +1,41 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_ACL_H_ +#define SANDBOX_SRC_ACL_H_ + +#include + +#include "base/memory/scoped_ptr.h" +#include "sandbox/src/sid.h" + +namespace sandbox { + +// Returns the default dacl from the token passed in. +bool GetDefaultDacl(HANDLE token, + scoped_ptr_malloc* default_dacl); + +// Appends an ACE represented by |sid| and |access| to |old_dacl|. If the +// function succeeds, new_dacl contains the new dacl and must be freed using +// LocalFree. +bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MASK access, + ACL** new_dacl); + +// Adds and ACE represented by |sid| and |access| to the default dacl present +// in the token. +bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access); + +// Adds an ACE represented by the user sid and |access| to the default dacl +// present in the token. +bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access); + +// Adds an ACE represented by |known_sid| and |access| to the dacl of the kernel +// object referenced by |object|. +bool AddKnownSidToKernelObject(HANDLE object, const Sid& sid, + ACCESS_MASK access); + +} // namespace sandbox + + +#endif // SANDBOX_SRC_ACL_H_ diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc new file mode 100644 index 0000000..7f46abe --- /dev/null +++ b/sandbox/win/src/broker_services.cc @@ -0,0 +1,405 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/broker_services.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/platform_thread.h" +#include "base/win/scoped_handle.h" +#include "base/win/scoped_process_information.h" +#include "sandbox/src/sandbox_policy_base.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/target_process.h" +#include "sandbox/src/win2k_threadpool.h" +#include "sandbox/src/win_utils.h" + +namespace { + +// Utility function to associate a completion port to a job object. +bool AssociateCompletionPort(HANDLE job, HANDLE port, void* key) { + JOBOBJECT_ASSOCIATE_COMPLETION_PORT job_acp = { key, port }; + return ::SetInformationJobObject(job, + JobObjectAssociateCompletionPortInformation, + &job_acp, sizeof(job_acp))? true : false; +} + +// Utility function to do the cleanup necessary when something goes wrong +// while in SpawnTarget and we must terminate the target process. +sandbox::ResultCode SpawnCleanup(sandbox::TargetProcess* target, DWORD error) { + if (0 == error) + error = ::GetLastError(); + + target->Terminate(); + delete target; + ::SetLastError(error); + return sandbox::SBOX_ERROR_GENERIC; +} + +// the different commands that you can send to the worker thread that +// executes TargetEventsThread(). +enum { + THREAD_CTRL_NONE, + THREAD_CTRL_REMOVE_PEER, + THREAD_CTRL_QUIT, + THREAD_CTRL_LAST, +}; + +// Helper structure that allows the Broker to associate a job notification +// with a job object and with a policy. +struct JobTracker { + HANDLE job; + sandbox::PolicyBase* policy; + JobTracker(HANDLE cjob, sandbox::PolicyBase* cpolicy) + : job(cjob), policy(cpolicy) { + } +}; + +// Helper structure that allows the broker to track peer processes +struct PeerTracker { + HANDLE wait_object; + base::win::ScopedHandle process; + DWORD id; + HANDLE job_port; + PeerTracker(DWORD process_id, HANDLE broker_job_port) + : wait_object(NULL), id(process_id), job_port(broker_job_port) { + } +}; + +void DeregisterPeerTracker(PeerTracker* peer) { + // Deregistration shouldn't fail, but we leak rather than crash if it does. + if (::UnregisterWaitEx(peer->wait_object, INVALID_HANDLE_VALUE)) { + delete peer; + } else { + NOTREACHED(); + } +} + +} // namespace + +namespace sandbox { + +BrokerServicesBase::BrokerServicesBase() + : thread_pool_(NULL), job_port_(NULL), no_targets_(NULL), + job_thread_(NULL) { +} + +// The broker uses a dedicated worker thread that services the job completion +// port to perform policy notifications and associated cleanup tasks. +ResultCode BrokerServicesBase::Init() { + if ((NULL != job_port_) || (NULL != thread_pool_)) + return SBOX_ERROR_UNEXPECTED_CALL; + + ::InitializeCriticalSection(&lock_); + + job_port_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (NULL == job_port_) + return SBOX_ERROR_GENERIC; + + no_targets_ = ::CreateEventW(NULL, TRUE, FALSE, NULL); + + job_thread_ = ::CreateThread(NULL, 0, // Default security and stack. + TargetEventsThread, this, NULL, NULL); + if (NULL == job_thread_) + return SBOX_ERROR_GENERIC; + + return SBOX_ALL_OK; +} + +// The destructor should only be called when the Broker process is terminating. +// Since BrokerServicesBase is a singleton, this is called from the CRT +// termination handlers, if this code lives on a DLL it is called during +// DLL_PROCESS_DETACH in other words, holding the loader lock, so we cannot +// wait for threads here. +BrokerServicesBase::~BrokerServicesBase() { + // If there is no port Init() was never called successfully. + if (!job_port_) + return; + + // Closing the port causes, that no more Job notifications are delivered to + // the worker thread and also causes the thread to exit. This is what we + // want to do since we are going to close all outstanding Jobs and notifying + // the policy objects ourselves. + ::PostQueuedCompletionStatus(job_port_, 0, THREAD_CTRL_QUIT, FALSE); + ::CloseHandle(job_port_); + + if (WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_, 1000)) { + // Cannot clean broker services. + NOTREACHED(); + return; + } + + JobTrackerList::iterator it; + for (it = tracker_list_.begin(); it != tracker_list_.end(); ++it) { + JobTracker* tracker = (*it); + FreeResources(tracker); + delete tracker; + } + ::CloseHandle(job_thread_); + delete thread_pool_; + ::CloseHandle(no_targets_); + + // Cancel the wait events and delete remaining peer trackers. + for (PeerTrackerMap::iterator it = peer_map_.begin(); + it != peer_map_.end(); ++it) { + DeregisterPeerTracker(it->second); + } + + // If job_port_ isn't NULL, assumes that the lock has been initialized. + if (job_port_) + ::DeleteCriticalSection(&lock_); +} + +TargetPolicy* BrokerServicesBase::CreatePolicy() { + // If you change the type of the object being created here you must also + // change the downcast to it in SpawnTarget(). + return new PolicyBase; +} + +void BrokerServicesBase::FreeResources(JobTracker* tracker) { + if (NULL != tracker->policy) { + BOOL res = ::TerminateJobObject(tracker->job, SBOX_ALL_OK); + DCHECK(res); + // Closing the job causes the target process to be destroyed so this + // needs to happen before calling OnJobEmpty(). + res = ::CloseHandle(tracker->job); + DCHECK(res); + // In OnJobEmpty() we don't actually use the job handle directly. + tracker->policy->OnJobEmpty(tracker->job); + tracker->policy->Release(); + tracker->policy = NULL; + } +} + +// The worker thread stays in a loop waiting for asynchronous notifications +// from the job objects. Right now we only care about knowing when the last +// process on a job terminates, but in general this is the place to tell +// the policy about events. +DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { + if (NULL == param) + return 1; + + base::PlatformThread::SetName("BrokerEvent"); + + BrokerServicesBase* broker = reinterpret_cast(param); + HANDLE port = broker->job_port_; + HANDLE no_targets = broker->no_targets_; + + int target_counter = 0; + ::ResetEvent(no_targets); + + while (true) { + DWORD events = 0; + ULONG_PTR key = 0; + LPOVERLAPPED ovl = NULL; + + if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE)) + // this call fails if the port has been closed before we have a + // chance to service the last packet which is 'exit' anyway so + // this is not an error. + return 1; + + if (key > THREAD_CTRL_LAST) { + // The notification comes from a job object. There are nine notifications + // that jobs can send and some of them depend on the job attributes set. + JobTracker* tracker = reinterpret_cast(key); + + switch (events) { + case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: { + // The job object has signaled that the last process associated + // with it has terminated. Assuming there is no way for a process + // to appear out of thin air in this job, it safe to assume that + // we can tell the policy to destroy the target object, and for + // us to release our reference to the policy object. + FreeResources(tracker); + break; + } + + case JOB_OBJECT_MSG_NEW_PROCESS: { + ++target_counter; + if (1 == target_counter) { + ::ResetEvent(no_targets); + } + break; + } + + case JOB_OBJECT_MSG_EXIT_PROCESS: + case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: { + { + AutoLock lock(&broker->lock_); + broker->child_process_ids_.erase(reinterpret_cast(ovl)); + } + --target_counter; + if (0 == target_counter) + ::SetEvent(no_targets); + + DCHECK(target_counter >= 0); + break; + } + + case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: { + break; + } + + default: { + NOTREACHED(); + break; + } + } + } else if (THREAD_CTRL_REMOVE_PEER == key) { + // Remove a process from our list of peers. + AutoLock lock(&broker->lock_); + PeerTrackerMap::iterator it = + broker->peer_map_.find(reinterpret_cast(ovl)); + DeregisterPeerTracker(it->second); + broker->peer_map_.erase(it); + } else if (THREAD_CTRL_QUIT == key) { + // The broker object is being destroyed so the thread needs to exit. + return 0; + } else { + // We have not implemented more commands. + NOTREACHED(); + } + } + + NOTREACHED(); + return 0; +} + +// SpawnTarget does all the interesting sandbox setup and creates the target +// process inside the sandbox. +ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, + const wchar_t* command_line, + TargetPolicy* policy, + PROCESS_INFORMATION* target_info) { + if (!exe_path) + return SBOX_ERROR_BAD_PARAMS; + + if (!policy) + return SBOX_ERROR_BAD_PARAMS; + + // Even though the resources touched by SpawnTarget can be accessed in + // multiple threads, the method itself cannot be called from more than + // 1 thread. This is to protect the global variables used while setting up + // the child process. + static DWORD thread_id = ::GetCurrentThreadId(); + DCHECK(thread_id == ::GetCurrentThreadId()); + + AutoLock lock(&lock_); + + // This downcast is safe as long as we control CreatePolicy() + PolicyBase* policy_base = static_cast(policy); + + // Construct the tokens and the job object that we are going to associate + // with the soon to be created target process. + HANDLE initial_token_temp; + HANDLE lockdown_token_temp; + DWORD win_result = policy_base->MakeTokens(&initial_token_temp, + &lockdown_token_temp); + base::win::ScopedHandle initial_token(initial_token_temp); + base::win::ScopedHandle lockdown_token(lockdown_token_temp); + + if (ERROR_SUCCESS != win_result) + return SBOX_ERROR_GENERIC; + + HANDLE job_temp; + win_result = policy_base->MakeJobObject(&job_temp); + base::win::ScopedHandle job(job_temp); + if (ERROR_SUCCESS != win_result) + return SBOX_ERROR_GENERIC; + + if (ERROR_ALREADY_EXISTS == ::GetLastError()) + return SBOX_ERROR_GENERIC; + + // Construct the thread pool here in case it is expensive. + // The thread pool is shared by all the targets + if (NULL == thread_pool_) + thread_pool_ = new Win2kThreadPool(); + + // Create the TargetProces object and spawn the target suspended. Note that + // Brokerservices does not own the target object. It is owned by the Policy. + base::win::ScopedProcessInformation process_info; + TargetProcess* target = new TargetProcess(initial_token.Take(), + lockdown_token.Take(), + job, + thread_pool_); + + std::wstring desktop = policy_base->GetAlternateDesktop(); + + win_result = target->Create(exe_path, command_line, + desktop.empty() ? NULL : desktop.c_str(), + &process_info); + if (ERROR_SUCCESS != win_result) + return SpawnCleanup(target, win_result); + + // Now the policy is the owner of the target. + if (!policy_base->AddTarget(target)) { + return SpawnCleanup(target, 0); + } + + // We are going to keep a pointer to the policy because we'll call it when + // the job object generates notifications using the completion port. + policy_base->AddRef(); + scoped_ptr tracker(new JobTracker(job.Take(), policy_base)); + if (!AssociateCompletionPort(tracker->job, job_port_, tracker.get())) + return SpawnCleanup(target, 0); + // Save the tracker because in cleanup we might need to force closing + // the Jobs. + tracker_list_.push_back(tracker.release()); + child_process_ids_.insert(process_info.process_id()); + + *target_info = process_info.Take(); + return SBOX_ALL_OK; +} + + +ResultCode BrokerServicesBase::WaitForAllTargets() { + ::WaitForSingleObject(no_targets_, INFINITE); + return SBOX_ALL_OK; +} + +bool BrokerServicesBase::IsActiveTarget(DWORD process_id) { + AutoLock lock(&lock_); + return child_process_ids_.find(process_id) != child_process_ids_.end() || + peer_map_.find(process_id) != peer_map_.end(); +} + +VOID CALLBACK BrokerServicesBase::RemovePeer(PVOID parameter, BOOLEAN timeout) { + PeerTracker* peer = reinterpret_cast(parameter); + // Don't check the return code because we this may fail (safely) at shutdown. + ::PostQueuedCompletionStatus(peer->job_port, 0, THREAD_CTRL_REMOVE_PEER, + reinterpret_cast(peer->id)); +} + +ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) { + scoped_ptr peer(new PeerTracker(::GetProcessId(peer_process), + job_port_)); + if (!peer->id) + return SBOX_ERROR_GENERIC; + + HANDLE process_handle; + if (!::DuplicateHandle(::GetCurrentProcess(), peer_process, + ::GetCurrentProcess(), &process_handle, + SYNCHRONIZE, FALSE, 0)) { + return SBOX_ERROR_GENERIC; + } + peer->process.Set(process_handle); + + AutoLock lock(&lock_); + if (!peer_map_.insert(std::make_pair(peer->id, peer.get())).second) + return SBOX_ERROR_BAD_PARAMS; + + if (!::RegisterWaitForSingleObject( + &peer->wait_object, peer->process, RemovePeer, peer.get(), INFINITE, + WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD)) { + peer_map_.erase(peer->id); + return SBOX_ERROR_GENERIC; + } + + // Release the pointer since it will be cleaned up by the callback. + peer.release(); + return SBOX_ALL_OK; +} + +} // namespace sandbox diff --git a/sandbox/win/src/broker_services.h b/sandbox/win/src/broker_services.h new file mode 100644 index 0000000..1d9c730 --- /dev/null +++ b/sandbox/win/src/broker_services.h @@ -0,0 +1,113 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_BROKER_SERVICES_H__ +#define SANDBOX_SRC_BROKER_SERVICES_H__ + +#include +#include +#include +#include "base/basictypes.h" +#include "base/win/scoped_handle.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/job.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sharedmem_ipc_server.h" +#include "sandbox/src/win2k_threadpool.h" +#include "sandbox/src/win_utils.h" + +namespace { + +struct JobTracker; +struct PeerTracker; + +} // namespace + +namespace sandbox { + +class PolicyBase; + +// BrokerServicesBase --------------------------------------------------------- +// Broker implementation version 0 +// +// This is an implementation of the interface BrokerServices and +// of the associated TargetProcess interface. In this implementation +// TargetProcess is a friend of BrokerServices where the later manages a +// collection of the former. +class BrokerServicesBase : public BrokerServices, + public SingletonBase { + public: + BrokerServicesBase(); + + ~BrokerServicesBase(); + + // The next five methods are the BrokerServices interface + virtual ResultCode Init(); + + virtual TargetPolicy* CreatePolicy(); + + virtual ResultCode SpawnTarget(const wchar_t* exe_path, + const wchar_t* command_line, + TargetPolicy* policy, + PROCESS_INFORMATION* target); + + virtual ResultCode WaitForAllTargets(); + + virtual ResultCode AddTargetPeer(HANDLE peer_process); + + // Checks if the supplied process ID matches one of the broker's active + // target processes + // Returns: + // true if there is an active target process for this ID, otherwise false. + bool IsActiveTarget(DWORD process_id); + + private: + // Releases the Job and notifies the associated Policy object to its + // resources as well. + static void FreeResources(JobTracker* tracker); + + // The routine that the worker thread executes. It is in charge of + // notifications and cleanup-related tasks. + static DWORD WINAPI TargetEventsThread(PVOID param); + + // Removes a target peer from the process list if it expires. + static VOID CALLBACK RemovePeer(PVOID parameter, BOOLEAN timeout); + + // The completion port used by the job objects to communicate events to + // the worker thread. + HANDLE job_port_; + + // Handle to a manual-reset event that is signaled when the total target + // process count reaches zero. + HANDLE no_targets_; + + // Handle to the worker thread that reacts to job notifications. + HANDLE job_thread_; + + // Lock used to protect the list of targets from being modified by 2 + // threads at the same time. + CRITICAL_SECTION lock_; + + // provides a pool of threads that are used to wait on the IPC calls. + ThreadProvider* thread_pool_; + + // List of the trackers for closing and cleanup purposes. + typedef std::list JobTrackerList; + JobTrackerList tracker_list_; + + // Maps peer process IDs to the saved handle and wait event. + // Prevents peer callbacks from accessing the broker after destruction. + typedef std::map PeerTrackerMap; + PeerTrackerMap peer_map_; + + // Provides a fast lookup to identify sandboxed processes. + std::set child_process_ids_; + + DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase); +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_BROKER_SERVICES_H__ diff --git a/sandbox/win/src/crosscall_client.h b/sandbox/win/src/crosscall_client.h new file mode 100644 index 0000000..e92c1d1 --- /dev/null +++ b/sandbox/win/src/crosscall_client.h @@ -0,0 +1,483 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_CROSSCALL_CLIENT_H_ +#define SANDBOX_SRC_CROSSCALL_CLIENT_H_ + +#include "sandbox/src/crosscall_params.h" +#include "sandbox/src/sandbox.h" + +// This header defines the CrossCall(..) family of templated functions +// Their purpose is to simulate the syntax of regular call but to generate +// and IPC from the client-side. +// +// The basic pattern is to +// 1) use template argument deduction to compute the size of each +// parameter and the appropriate copy method +// 2) pack the parameters in the appropriate ActualCallParams< > object +// 3) call the IPC interface IPCProvider::DoCall( ) +// +// The general interface of CrossCall is: +// ResultCode CrossCall(IPCProvider& ipc_provider, +// uint32 tag, +// const Par1& p1, const Par2& p2,...pn +// CrossCallReturn* answer) +// +// where: +// ipc_provider: is a specific implementation of the ipc transport see +// sharedmem_ipc_server.h for an example. +// tag : is the unique id for this IPC call. Is used to route the call to +// the appropriate service. +// p1, p2,.. pn : The input parameters of the IPC. Use only simple types +// and wide strings (can add support for others). +// answer : If the IPC was successful. The server-side answer is here. The +// interpretation of the answer is private to client and server. +// +// The return value is ALL_OK if the IPC was delivered to the server, other +// return codes indicate that the IPC transport failed to deliver it. +namespace sandbox { + +// this is the assumed channel size. This can be overridden in a given +// IPC implementation. +const uint32 kIPCChannelSize = 1024; + +// The copy helper uses templates to deduce the appropriate copy function to +// copy the input parameters in the buffer that is going to be send across the +// IPC. These template facility can be made more sophisticated as need arises. + +// The default copy helper. It catches the general case where no other +// specialized template matches better. We set the type to ULONG_TYPE, so this +// only works with objects whose size is 32 bits. +template +class CopyHelper { + public: + CopyHelper(const T& t) : t_(t) {} + + // Returns the pointer to the start of the input. + const void* GetStart() const { + return &t_; + } + + // Update the stored value with the value in the buffer. This is not + // supported for this type. + bool Update(void* buffer) { + // Not supported; + return true; + } + + // Returns the size of the input in bytes. + uint32 GetSize() const { + return sizeof(T); + } + + // Returns true if the current type is used as an In or InOut parameter. + bool IsInOut() { + return false; + } + + // Returns this object's type. + ArgType GetType() { + COMPILE_ASSERT(sizeof(T) == sizeof(uint32), need_specialization); + return ULONG_TYPE; + } + + private: + const T& t_; +}; + +// This copy helper template specialization if for the void pointer +// case both 32 and 64 bit. +template<> +class CopyHelper { + public: + CopyHelper(void* t) : t_(t) {} + + // Returns the pointer to the start of the input. + const void* GetStart() const { + return &t_; + } + + // Update the stored value with the value in the buffer. This is not + // supported for this type. + bool Update(void* buffer) { + // Not supported; + return true; + } + + // Returns the size of the input in bytes. + uint32 GetSize() const { + return sizeof(t_); + } + + // Returns true if the current type is used as an In or InOut parameter. + bool IsInOut() { + return false; + } + + // Returns this object's type. + ArgType GetType() { + return VOIDPTR_TYPE; + } + + private: + const void* t_; +}; + +// This copy helper template specialization catches the cases where the +// parameter is a pointer to a string. +template<> +class CopyHelper { + public: + CopyHelper(const wchar_t* t) + : t_(t) { + } + + // Returns the pointer to the start of the string. + const void* GetStart() const { + return t_; + } + + // Update the stored value with the value in the buffer. This is not + // supported for this type. + bool Update(void* buffer) { + // Not supported; + return true; + } + + // Returns the size of the string in bytes. We define a NULL string to + // be of zero length. + uint32 GetSize() const { + __try { + return (!t_) ? 0 : static_cast(StringLength(t_) * sizeof(t_[0])); + } + __except(EXCEPTION_EXECUTE_HANDLER) { + return kuint32max; + } + } + + // Returns true if the current type is used as an In or InOut parameter. + bool IsInOut() { + return false; + } + + ArgType GetType() { + return WCHAR_TYPE; + } + + private: + // We provide our not very optimized version of wcslen(), since we don't + // want to risk having the linker use the version in the CRT since the CRT + // might not be present when we do an early IPC call. + static size_t __cdecl StringLength(const wchar_t* wcs) { + const wchar_t *eos = wcs; + while (*eos++); + return static_cast(eos - wcs - 1); + } + + const wchar_t* t_; +}; + +// Specialization for non-const strings. We just reuse the implementation of the +// const string specialization. +template<> +class CopyHelper : public CopyHelper { + public: + typedef CopyHelper Base; + CopyHelper(wchar_t* t) : Base(t) {} + + const void* GetStart() const { + return Base::GetStart(); + } + + bool Update(void* buffer) { + return Base::Update(buffer); + } + + uint32 GetSize() const { + return Base::GetSize(); + } + + bool IsInOut() { + return Base::IsInOut(); + } + + ArgType GetType() { + return Base::GetType(); + } +}; + +// Specialization for wchar_t arrays strings. We just reuse the implementation +// of the const string specialization. +template +class CopyHelper : public CopyHelper { + public: + typedef const wchar_t array[n]; + typedef CopyHelper Base; + CopyHelper(array t) : Base(t) {} + + const void* GetStart() const { + return Base::GetStart(); + } + + bool Update(void* buffer) { + return Base::Update(buffer); + } + + uint32 GetSize() const { + return Base::GetSize(); + } + + bool IsInOut() { + return Base::IsInOut(); + } + + ArgType GetType() { + return Base::GetType(); + } +}; + +// Generic encapsulation class containing a pointer to a buffer and the +// size of the buffer. It is used by the IPC to be able to pass in/out +// parameters. +class InOutCountedBuffer : public CountedBuffer { + public: + InOutCountedBuffer(void* buffer, uint32 size) : CountedBuffer(buffer, size) {} +}; + +// This copy helper template specialization catches the cases where the +// parameter is a an input/output buffer. +template<> +class CopyHelper { + public: + CopyHelper(const InOutCountedBuffer t) : t_(t) {} + + // Returns the pointer to the start of the string. + const void* GetStart() const { + return t_.Buffer(); + } + + // Updates the buffer with the value from the new buffer in parameter. + bool Update(void* buffer) { + // We are touching user memory, this has to be done from inside a try + // except. + __try { + memcpy(t_.Buffer(), buffer, t_.Size()); + } + __except(EXCEPTION_EXECUTE_HANDLER) { + return false; + } + return true; + } + + // Returns the size of the string in bytes. We define a NULL string to + // be of zero length. + uint32 GetSize() const { + return t_.Size(); + } + + // Returns true if the current type is used as an In or InOut parameter. + bool IsInOut() { + return true; + } + + ArgType GetType() { + return INOUTPTR_TYPE; + } + + private: + const InOutCountedBuffer t_; +}; + +// The following two macros make it less error prone the generation +// of CrossCall functions with ever more input parameters. + +#define XCALL_GEN_PARAMS_OBJ(num, params) \ + typedef ActualCallParams ActualParams; \ + void* raw_mem = ipc_provider.GetBuffer(); \ + if (NULL == raw_mem) \ + return SBOX_ERROR_NO_SPACE; \ + ActualParams* params = new(raw_mem) ActualParams(tag); + +#define XCALL_GEN_COPY_PARAM(num, params) \ + COMPILE_ASSERT(kMaxIpcParams >= num, too_many_parameters); \ + CopyHelper ch##num(p##num); \ + if (!params->CopyParamIn(num - 1, ch##num.GetStart(), ch##num.GetSize(), \ + ch##num.IsInOut(), ch##num.GetType())) \ + return SBOX_ERROR_NO_SPACE; + +#define XCALL_GEN_UPDATE_PARAM(num, params) \ + if (!ch##num.Update(params->GetParamPtr(num-1))) {\ + ipc_provider.FreeBuffer(raw_mem); \ + return SBOX_ERROR_BAD_PARAMS; \ + } + +#define XCALL_GEN_FREE_CHANNEL() \ + ipc_provider.FreeBuffer(raw_mem); + +// CrossCall template with one input parameter +template +ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, + CrossCallReturn* answer) { + XCALL_GEN_PARAMS_OBJ(1, call_params); + XCALL_GEN_COPY_PARAM(1, call_params); + + ResultCode result = ipc_provider.DoCall(call_params, answer); + + if (SBOX_ERROR_CHANNEL_ERROR != result) { + XCALL_GEN_UPDATE_PARAM(1, call_params); + XCALL_GEN_FREE_CHANNEL(); + } + + return result; +} + +// CrossCall template with two input parameters. +template +ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, + const Par2& p2, CrossCallReturn* answer) { + XCALL_GEN_PARAMS_OBJ(2, call_params); + XCALL_GEN_COPY_PARAM(1, call_params); + XCALL_GEN_COPY_PARAM(2, call_params); + + ResultCode result = ipc_provider.DoCall(call_params, answer); + + if (SBOX_ERROR_CHANNEL_ERROR != result) { + XCALL_GEN_UPDATE_PARAM(1, call_params); + XCALL_GEN_UPDATE_PARAM(2, call_params); + XCALL_GEN_FREE_CHANNEL(); + } + return result; +} + +// CrossCall template with three input parameters. +template +ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, + const Par2& p2, const Par3& p3, CrossCallReturn* answer) { + XCALL_GEN_PARAMS_OBJ(3, call_params); + XCALL_GEN_COPY_PARAM(1, call_params); + XCALL_GEN_COPY_PARAM(2, call_params); + XCALL_GEN_COPY_PARAM(3, call_params); + + ResultCode result = ipc_provider.DoCall(call_params, answer); + + if (SBOX_ERROR_CHANNEL_ERROR != result) { + XCALL_GEN_UPDATE_PARAM(1, call_params); + XCALL_GEN_UPDATE_PARAM(2, call_params); + XCALL_GEN_UPDATE_PARAM(3, call_params); + XCALL_GEN_FREE_CHANNEL(); + } + return result; +} + +// CrossCall template with four input parameters. +template +ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, + const Par2& p2, const Par3& p3, const Par4& p4, + CrossCallReturn* answer) { + XCALL_GEN_PARAMS_OBJ(4, call_params); + XCALL_GEN_COPY_PARAM(1, call_params); + XCALL_GEN_COPY_PARAM(2, call_params); + XCALL_GEN_COPY_PARAM(3, call_params); + XCALL_GEN_COPY_PARAM(4, call_params); + + ResultCode result = ipc_provider.DoCall(call_params, answer); + + if (SBOX_ERROR_CHANNEL_ERROR != result) { + XCALL_GEN_UPDATE_PARAM(1, call_params); + XCALL_GEN_UPDATE_PARAM(2, call_params); + XCALL_GEN_UPDATE_PARAM(3, call_params); + XCALL_GEN_UPDATE_PARAM(4, call_params); + XCALL_GEN_FREE_CHANNEL(); + } + return result; +} + +// CrossCall template with five input parameters. +template +ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, + const Par2& p2, const Par3& p3, const Par4& p4, + const Par5& p5, CrossCallReturn* answer) { + XCALL_GEN_PARAMS_OBJ(5, call_params); + XCALL_GEN_COPY_PARAM(1, call_params); + XCALL_GEN_COPY_PARAM(2, call_params); + XCALL_GEN_COPY_PARAM(3, call_params); + XCALL_GEN_COPY_PARAM(4, call_params); + XCALL_GEN_COPY_PARAM(5, call_params); + + ResultCode result = ipc_provider.DoCall(call_params, answer); + + if (SBOX_ERROR_CHANNEL_ERROR != result) { + XCALL_GEN_UPDATE_PARAM(1, call_params); + XCALL_GEN_UPDATE_PARAM(2, call_params); + XCALL_GEN_UPDATE_PARAM(3, call_params); + XCALL_GEN_UPDATE_PARAM(4, call_params); + XCALL_GEN_UPDATE_PARAM(5, call_params); + XCALL_GEN_FREE_CHANNEL(); + } + return result; +} + +// CrossCall template with six input parameters. +template +ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, + const Par2& p2, const Par3& p3, const Par4& p4, + const Par5& p5, const Par6& p6, CrossCallReturn* answer) { + XCALL_GEN_PARAMS_OBJ(6, call_params); + XCALL_GEN_COPY_PARAM(1, call_params); + XCALL_GEN_COPY_PARAM(2, call_params); + XCALL_GEN_COPY_PARAM(3, call_params); + XCALL_GEN_COPY_PARAM(4, call_params); + XCALL_GEN_COPY_PARAM(5, call_params); + XCALL_GEN_COPY_PARAM(6, call_params); + + ResultCode result = ipc_provider.DoCall(call_params, answer); + + if (SBOX_ERROR_CHANNEL_ERROR != result) { + XCALL_GEN_UPDATE_PARAM(1, call_params); + XCALL_GEN_UPDATE_PARAM(2, call_params); + XCALL_GEN_UPDATE_PARAM(3, call_params); + XCALL_GEN_UPDATE_PARAM(4, call_params); + XCALL_GEN_UPDATE_PARAM(5, call_params); + XCALL_GEN_UPDATE_PARAM(6, call_params); + XCALL_GEN_FREE_CHANNEL(); + } + return result; +} + +// CrossCall template with seven input parameters. +template +ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, + const Par2& p2, const Par3& p3, const Par4& p4, + const Par5& p5, const Par6& p6, const Par7& p7, + CrossCallReturn* answer) { + XCALL_GEN_PARAMS_OBJ(7, call_params); + XCALL_GEN_COPY_PARAM(1, call_params); + XCALL_GEN_COPY_PARAM(2, call_params); + XCALL_GEN_COPY_PARAM(3, call_params); + XCALL_GEN_COPY_PARAM(4, call_params); + XCALL_GEN_COPY_PARAM(5, call_params); + XCALL_GEN_COPY_PARAM(6, call_params); + XCALL_GEN_COPY_PARAM(7, call_params); + + ResultCode result = ipc_provider.DoCall(call_params, answer); + + if (SBOX_ERROR_CHANNEL_ERROR != result) { + XCALL_GEN_UPDATE_PARAM(1, call_params); + XCALL_GEN_UPDATE_PARAM(2, call_params); + XCALL_GEN_UPDATE_PARAM(3, call_params); + XCALL_GEN_UPDATE_PARAM(4, call_params); + XCALL_GEN_UPDATE_PARAM(5, call_params); + XCALL_GEN_UPDATE_PARAM(6, call_params); + XCALL_GEN_UPDATE_PARAM(7, call_params); + XCALL_GEN_FREE_CHANNEL(); + } + return result; +} +} // namespace sandbox + +#endif // SANDBOX_SRC_CROSSCALL_CLIENT_H__ diff --git a/sandbox/win/src/crosscall_params.h b/sandbox/win/src/crosscall_params.h new file mode 100644 index 0000000..e4c047b --- /dev/null +++ b/sandbox/win/src/crosscall_params.h @@ -0,0 +1,293 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_CROSSCALL_PARAMS_H__ +#define SANDBOX_SRC_CROSSCALL_PARAMS_H__ + +#include +#include + +#include + +#include "base/basictypes.h" +#include "sandbox/src/internal_types.h" +#include "sandbox/src/sandbox_types.h" + +namespace { + +// Increases |value| until there is no need for padding given an int64 +// alignment. Returns the increased value. +uint32 Align(uint32 value) { + uint32 alignment = sizeof(int64); + return ((value + alignment - 1) / alignment) * alignment; +} + +} +// This header is part of CrossCall: the sandbox inter-process communication. +// This header defines the basic types used both in the client IPC and in the +// server IPC code. CrossCallParams and ActualCallParams model the input +// parameters of an IPC call and CrossCallReturn models the output params and +// the return value. +// +// An IPC call is defined by its 'tag' which is a (uint32) unique identifier +// that is used to route the IPC call to the proper server. Every tag implies +// a complete call signature including the order and type of each parameter. +// +// Like most IPC systems. CrossCall is designed to take as inputs 'simple' +// types such as integers and strings. Classes, generic arrays or pointers to +// them are not supported. +// +// Another limitation of CrossCall is that the return value and output +// parameters can only be uint32 integers. Returning complex structures or +// strings is not supported. + +namespace sandbox { + +// max number of extended return parameters. See CrossCallReturn +const size_t kExtendedReturnCount = 8; + +// Union of multiple types to be used as extended results +// in the CrossCallReturn. +union MultiType { + uint32 unsigned_int; + void* pointer; + HANDLE handle; + ULONG_PTR ulong_ptr; +}; + +// Maximum number of IPC parameters currently supported. +// To increase this value, we have to: +// - Add another Callback typedef to Dispatcher. +// - Add another case to the switch on SharedMemIPCServer::InvokeCallback. +// - Add another case to the switch in GetActualAndMaxBufferSize +const int kMaxIpcParams = 9; + +// Contains the information about a parameter in the ipc buffer. +struct ParamInfo { + ArgType type_; + uint32 offset_; + uint32 size_; +}; + +// Models the return value and the return parameters of an IPC call +// currently limited to one status code and eight generic return values +// which cannot be pointers to other data. For x64 ports this structure +// might have to use other integer types. +struct CrossCallReturn { + // the IPC tag. It should match the original IPC tag. + uint32 tag; + // The result of the IPC operation itself. + ResultCode call_outcome; + // the result of the IPC call as executed in the server. The interpretation + // of this value depends on the specific service. + union { + NTSTATUS nt_status; + DWORD win32_result; + }; + // Number of extended return values. + uint32 extended_count; + // for calls that should return a windows handle. It is found here. + HANDLE handle; + // The array of extended values. + MultiType extended[kExtendedReturnCount]; +}; + +// CrossCallParams base class that models the input params all packed in a +// single compact memory blob. The representation can vary but in general a +// given child of this class is meant to represent all input parameters +// necessary to make a IPC call. +// +// This class cannot have virtual members because its assumed the IPC +// parameters start from the 'this' pointer to the end, which is defined by +// one of the subclasses +// +// Objects of this class cannot be constructed directly. Only derived +// classes have the proper knowledge to construct it. +class CrossCallParams { + public: + // Returns the tag (ipc unique id) associated with this IPC. + uint32 GetTag() const { + return tag_; + } + + // Returns the beggining of the buffer where the IPC params can be stored. + // prior to an IPC call + const void* GetBuffer() const { + return this; + } + + // Returns how many parameter this IPC call should have. + const uint32 GetParamsCount() const { + return params_count_; + } + + // Returns a pointer to the CrossCallReturn structure. + CrossCallReturn* GetCallReturn() { + return &call_return; + } + + // Returns TRUE if this call contains InOut parameters. + const bool IsInOut() const { + return (1 == is_in_out_); + } + + // Tells the CrossCall object if it contains InOut parameters. + void SetIsInOut(bool value) { + if (value) + is_in_out_ = 1; + else + is_in_out_ = 0; + } + + protected: + // constructs the IPC call params. Called only from the derived classes + CrossCallParams(uint32 tag, uint32 params_count) + : tag_(tag), + params_count_(params_count), + is_in_out_(0) { + } + + private: + uint32 tag_; + uint32 is_in_out_; + CrossCallReturn call_return; + const uint32 params_count_; + DISALLOW_COPY_AND_ASSIGN(CrossCallParams); +}; + +// ActualCallParams models an specific IPC call parameters with respect to the +// storage allocation that the packed parameters should need. +// NUMBER_PARAMS: the number of parameters, valid from 1 to N +// BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take, +// typically the block size is defined by the channel size of the underlying +// ipc mechanism. +// In practice this class is used to levergage C++ capacity to properly +// calculate sizes and displacements given the possibility of the packed params +// blob to be complex. +// +// As is, this class assumes that the layout of the blob is as follows. Assume +// that NUMBER_PARAMS = 2 and a 32-bit build: +// +// [ tag 4 bytes] +// [ IsOnOut 4 bytes] +// [ call return 52 bytes] +// [ params count 4 bytes] +// [ parameter 0 type 4 bytes] +// [ parameter 0 offset 4 bytes] ---delta to ---\ +// [ parameter 0 size 4 bytes] | +// [ parameter 1 type 4 bytes] | +// [ parameter 1 offset 4 bytes] ---------------|--\ +// [ parameter 1 size 4 bytes] | | +// [ parameter 2 type 4 bytes] | | +// [ parameter 2 offset 4 bytes] ----------------------\ +// [ parameter 2 size 4 bytes] | | | +// |---------------------------| | | | +// | value 0 (x bytes) | <--------------/ | | +// | value 1 (y bytes) | <-----------------/ | +// | | | +// | end of buffer | <---------------------/ +// |---------------------------| +// +// Note that the actual number of params is NUMBER_PARAMS + 1 +// so that the size of each actual param can be computed from the difference +// between one parameter and the next down. The offset of the last param +// points to the end of the buffer and the type and size are undefined. +// +template +class ActualCallParams : public CrossCallParams { + public: + // constructor. Pass the ipc unique tag as input + explicit ActualCallParams(uint32 tag) + : CrossCallParams(tag, NUMBER_PARAMS) { + param_info_[0].offset_ = parameters_ - reinterpret_cast(this); + } + + // Testing-only constructor. Allows setting the |number_params| to a + // wrong value. + ActualCallParams(uint32 tag, uint32 number_params) + : CrossCallParams(tag, number_params) { + param_info_[0].offset_ = parameters_ - reinterpret_cast(this); + } + + // Testing-only method. Allows setting the apparent size to a wrong value. + // returns the previous size. + uint32 OverrideSize(uint32 new_size) { + uint32 previous_size = param_info_[NUMBER_PARAMS].offset_; + param_info_[NUMBER_PARAMS].offset_ = new_size; + return previous_size; + } + + // Copies each paramter into the internal buffer. For each you must supply: + // index: 0 for the first param, 1 for the next an so on + bool CopyParamIn(uint32 index, const void* parameter_address, uint32 size, + bool is_in_out, ArgType type) { + if (index >= NUMBER_PARAMS) { + return false; + } + + if (kuint32max == size) { + // Memory error while getting the size. + return false; + } + + if (size && !parameter_address) { + return false; + } + + if (param_info_[index].offset_ > sizeof(*this)) { + // It does not fit, abort copy. + return false; + } + + char* dest = reinterpret_cast(this) + param_info_[index].offset_; + + // We might be touching user memory, this has to be done from inside a try + // except. + __try { + memcpy(dest, parameter_address, size); + } + __except(EXCEPTION_EXECUTE_HANDLER) { + return false; + } + + // Set the flag to tell the broker to update the buffer once the call is + // made. + if (is_in_out) + SetIsInOut(true); + + param_info_[index + 1].offset_ = Align(param_info_[index].offset_ + + size); + param_info_[index].size_ = size; + param_info_[index].type_ = type; + return true; + } + + // Returns a pointer to a parameter in the memory section. + void* GetParamPtr(size_t index) { + return reinterpret_cast(this) + param_info_[index].offset_; + } + + // Returns the total size of the buffer. Only valid once all the paramters + // have been copied in with CopyParamIn. + uint32 GetSize() const { + return param_info_[NUMBER_PARAMS].offset_; + } + + protected: + ActualCallParams() : CrossCallParams(0, NUMBER_PARAMS) { } + + private: + ParamInfo param_info_[NUMBER_PARAMS + 1]; + char parameters_[BLOCK_SIZE - sizeof(CrossCallParams) + - sizeof(ParamInfo) * (NUMBER_PARAMS + 1)]; + DISALLOW_COPY_AND_ASSIGN(ActualCallParams); +}; + +COMPILE_ASSERT(sizeof(ActualCallParams<1, 1024>) == 1024, bad_size_buffer); +COMPILE_ASSERT(sizeof(ActualCallParams<2, 1024>) == 1024, bad_size_buffer); +COMPILE_ASSERT(sizeof(ActualCallParams<3, 1024>) == 1024, bad_size_buffer); + +} // namespace sandbox + +#endif // SANDBOX_SRC_CROSSCALL_PARAMS_H__ diff --git a/sandbox/win/src/crosscall_server.cc b/sandbox/win/src/crosscall_server.cc new file mode 100644 index 0000000..f40b677 --- /dev/null +++ b/sandbox/win/src/crosscall_server.cc @@ -0,0 +1,283 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/crosscall_params.h" +#include "sandbox/src/crosscall_client.h" +#include "base/logging.h" + +// This code performs the ipc message validation. Potential security flaws +// on the ipc are likelier to be found in this code than in the rest of +// the ipc code. + +namespace { + +// The buffer for a message must match the max channel size. +const size_t kMaxBufferSize = sandbox::kIPCChannelSize; + +} + +namespace sandbox { + +// Returns the actual size for the parameters in an IPC buffer. Returns +// zero if the |param_count| is zero or too big. +uint32 GetActualBufferSize(uint32 param_count, void* buffer_base) { + // The template types are used to calculate the maximum expected size. + typedef ActualCallParams<1, kMaxBufferSize> ActualCP1; + typedef ActualCallParams<2, kMaxBufferSize> ActualCP2; + typedef ActualCallParams<3, kMaxBufferSize> ActualCP3; + typedef ActualCallParams<4, kMaxBufferSize> ActualCP4; + typedef ActualCallParams<5, kMaxBufferSize> ActualCP5; + typedef ActualCallParams<6, kMaxBufferSize> ActualCP6; + typedef ActualCallParams<7, kMaxBufferSize> ActualCP7; + typedef ActualCallParams<8, kMaxBufferSize> ActualCP8; + typedef ActualCallParams<9, kMaxBufferSize> ActualCP9; + + // Retrieve the actual size and the maximum size of the params buffer. + switch (param_count) { + case 0: + return 0; + case 1: + return reinterpret_cast(buffer_base)->GetSize(); + case 2: + return reinterpret_cast(buffer_base)->GetSize(); + case 3: + return reinterpret_cast(buffer_base)->GetSize(); + case 4: + return reinterpret_cast(buffer_base)->GetSize(); + case 5: + return reinterpret_cast(buffer_base)->GetSize(); + case 6: + return reinterpret_cast(buffer_base)->GetSize(); + case 7: + return reinterpret_cast(buffer_base)->GetSize(); + case 8: + return reinterpret_cast(buffer_base)->GetSize(); + case 9: + return reinterpret_cast(buffer_base)->GetSize(); + default: + NOTREACHED(); + return 0; + } +} + +CrossCallParamsEx::CrossCallParamsEx() + :CrossCallParams(0, 0) { +} + +// We override the delete operator because the object's backing memory +// is hand allocated in CreateFromBuffer. We don't override the new operator +// because the constructors are private so there is no way to mismatch +// new & delete. +void CrossCallParamsEx::operator delete(void* raw_memory) throw() { + if (NULL == raw_memory) { + // C++ standard allows 'delete 0' behavior. + return; + } + delete[] reinterpret_cast(raw_memory); +} + +// This function uses a SEH try block so cannot use C++ objects that +// have destructors or else you get Compiler Error C2712. So no DCHECKs +// inside this function. +CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base, + uint32 buffer_size, + uint32* output_size) { + // IMPORTANT: Everything inside buffer_base and derived from it such + // as param_count and declared_size is untrusted. + if (NULL == buffer_base) { + return NULL; + } + if (buffer_size < sizeof(CrossCallParams)) { + return NULL; + } + if (buffer_size > kMaxBufferSize) { + return NULL; + } + + char* backing_mem = NULL; + uint32 param_count = 0; + uint32 declared_size; + uint32 min_declared_size; + CrossCallParamsEx* copied_params = NULL; + + // Touching the untrusted buffer is done under a SEH try block. This + // will catch memory access violations so we don't crash. + __try { + CrossCallParams* call_params = + reinterpret_cast(buffer_base); + + // Check against the minimum size given the number of stated params + // if too small we bail out. + param_count = call_params->GetParamsCount(); + min_declared_size = sizeof(CrossCallParams) + + ((param_count + 1) * sizeof(ParamInfo)); + + if ((buffer_size < min_declared_size) || + (sizeof(CrossCallParamsEx) > min_declared_size)) { + // Minimal computed size bigger than existing buffer or param_count + // integer overflow. + return NULL; + } + + // Retrieve the declared size which if it fails returns 0. + declared_size = GetActualBufferSize(param_count, buffer_base); + + if ((declared_size > buffer_size) || + (declared_size < min_declared_size)) { + // Declared size is bigger than buffer or smaller than computed size + // or param_count 0 or bigger than 9. + return NULL; + } + + // Now we copy the actual amount of the message. + *output_size = declared_size; + backing_mem = new char[declared_size]; + copied_params = reinterpret_cast(backing_mem); + memcpy(backing_mem, call_params, declared_size); + + // Check params count in case it got changed right before the memcpy. + if (copied_params->GetParamsCount() != param_count) { + delete [] backing_mem; + return NULL; + } + + } __except(EXCEPTION_EXECUTE_HANDLER) { + // In case of a windows exception we know it occurred while touching the + // untrusted buffer so we bail out as is. + delete [] backing_mem; + return NULL; + } + + const char* last_byte = &backing_mem[declared_size]; + const char* first_byte = &backing_mem[min_declared_size]; + + // Verify here that all and each parameters make sense. This is done in the + // local copy. + for (uint32 ix =0; ix != param_count; ++ix) { + uint32 size = 0; + ArgType type; + char* address = reinterpret_cast( + copied_params->GetRawParameter(ix, &size, &type)); + if ((NULL == address) || // No null params. + (INVALID_TYPE >= type) || (LAST_TYPE <= type) || // Unknown type. + (address < backing_mem) || // Start cannot point before buffer. + (address < first_byte) || // Start cannot point too low. + (address > last_byte) || // Start cannot point past buffer. + ((address + size) < address) || // Invalid size. + ((address + size) > last_byte)) { // End cannot point past buffer. + // Malformed. + delete[] backing_mem; + return NULL; + } + } + // The parameter buffer looks good. + return copied_params; +} + +// Accessors to the parameters in the raw buffer. +void* CrossCallParamsEx::GetRawParameter(uint32 index, uint32* size, + ArgType* type) { + if (index >= GetParamsCount()) { + return NULL; + } + // The size is always computed from the parameter minus the next + // parameter, this works because the message has an extra parameter slot + *size = param_info_[index].size_; + *type = param_info_[index].type_; + + return param_info_[index].offset_ + reinterpret_cast(this); +} + +// Covers common case for 32 bit integers. +bool CrossCallParamsEx::GetParameter32(uint32 index, uint32* param) { + uint32 size = 0; + ArgType type; + void* start = GetRawParameter(index, &size, &type); + if ((NULL == start) || (4 != size) || (ULONG_TYPE != type)) { + return false; + } + // Copy the 4 bytes. + *(reinterpret_cast(param)) = *(reinterpret_cast(start)); + return true; +} + +bool CrossCallParamsEx::GetParameterVoidPtr(uint32 index, void** param) { + uint32 size = 0; + ArgType type; + void* start = GetRawParameter(index, &size, &type); + if ((NULL == start) || (sizeof(void*) != size) || (VOIDPTR_TYPE != type)) { + return false; + } + *param = *(reinterpret_cast(start)); + return true; +} + +// Covers the common case of reading a string. Note that the string is not +// scanned for invalid characters. +bool CrossCallParamsEx::GetParameterStr(uint32 index, std::wstring* string) { + uint32 size = 0; + ArgType type; + void* start = GetRawParameter(index, &size, &type); + if (WCHAR_TYPE != type) { + return false; + } + + // Check if this is an empty string. + if (size == 0) { + *string = L""; + return true; + } + + if ((NULL == start) || ((size % sizeof(wchar_t)) != 0)) { + return false; + } + string->append(reinterpret_cast(start), size/(sizeof(wchar_t))); + return true; +} + +bool CrossCallParamsEx::GetParameterPtr(uint32 index, uint32 expected_size, + void** pointer) { + uint32 size = 0; + ArgType type; + void* start = GetRawParameter(index, &size, &type); + + if ((size != expected_size) || (INOUTPTR_TYPE != type)) { + return false; + } + + if (NULL == start) { + return false; + } + + *pointer = start; + return true; +} + +void SetCallError(ResultCode error, CrossCallReturn* call_return) { + call_return->call_outcome = error; + call_return->extended_count = 0; +} + +void SetCallSuccess(CrossCallReturn* call_return) { + call_return->call_outcome = SBOX_ALL_OK; +} + +Dispatcher* Dispatcher::OnMessageReady(IPCParams* ipc, + CallbackGeneric* callback) { + DCHECK(callback); + std::vector::iterator it = ipc_calls_.begin(); + for (; it != ipc_calls_.end(); ++it) { + if (it->params.Matches(ipc)) { + *callback = it->callback; + return this; + } + } + return NULL; +} + +} // namespace sandbox diff --git a/sandbox/win/src/crosscall_server.h b/sandbox/win/src/crosscall_server.h new file mode 100644 index 0000000..59445f6 --- /dev/null +++ b/sandbox/win/src/crosscall_server.h @@ -0,0 +1,224 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_CROSSCALL_SERVER_H_ +#define SANDBOX_SRC_CROSSCALL_SERVER_H_ + +#include +#include +#include "base/basictypes.h" +#include "base/callback.h" +#include "sandbox/src/crosscall_params.h" + +// This is the IPC server interface for CrossCall: The IPC for the Sandbox +// On the server, CrossCall needs two things: +// 1) threads: Or better said, someone to provide them, that is what the +// ThreadProvider interface is defined for. These thread(s) are +// the ones that will actually execute the IPC data retrieval. +// +// 2) a dispatcher: This interface represents the way to route and process +// an IPC call given the IPC tag. +// +// The other class included here CrossCallParamsEx is the server side version +// of the CrossCallParams class of /sandbox/crosscall_params.h The difference +// is that the sever version is paranoid about the correctness of the IPC +// message and will do all sorts of verifications. +// +// A general diagram of the interaction is as follows: +// +// ------------ +// | | +// ThreadProvider <--(1)Register--| IPC | +// | | Implemen | +// | | -tation | +// (2) | | OnMessage +// IPC fired --callback ------>| |--(3)---> Dispatcher +// | | +// ------------ +// +// The IPC implementation sits as a middleman between the handling of the +// specifics of scheduling a thread to service the IPC and the multiple +// entities that can potentially serve each particular IPC. +namespace sandbox { + +class InterceptionManager; + +// This function signature is required as the callback when an IPC call fires. +// context: a user-defined pointer that was set using ThreadProvider +// reason: 0 if the callback was fired because of a timeout. +// 1 if the callback was fired because of an event. +typedef void (__stdcall * CrossCallIPCCallback)(void* context, + unsigned char reason); + +// ThreadProvider models a thread factory. The idea is to decouple thread +// creation and lifetime from the inner guts of the IPC. The contract is +// simple: +// - the IPC implementation calls RegisterWait with a waitable object that +// becomes signaled when an IPC arrives and needs to be serviced. +// - when the waitable object becomes signaled, the thread provider conjures +// a thread that calls the callback (CrossCallIPCCallback) function +// - the callback function tries its best not to block and return quickly +// and should not assume that the next callback will use the same thread +// - when the callback returns the ThreadProvider owns again the thread +// and can destroy it or keep it around. +class ThreadProvider { + public: + // Registers a waitable object with the thread provider. + // client: A number to associate with all the RegisterWait calls, typically + // this is the address of the caller object. This parameter cannot + // be zero. + // waitable_object : a kernel object that can be waited on + // callback: a function pointer which is the function that will be called + // when the waitable object fires + // context: a user-provider pointer that is passed back to the callback + // when its called + virtual bool RegisterWait(const void* client, HANDLE waitable_object, + CrossCallIPCCallback callback, + void* context) = 0; + + // Removes all the registrations done with the same cookie parameter. + // This frees internal thread pool resources. + virtual bool UnRegisterWaits(void* cookie) = 0; + virtual ~ThreadProvider() {} +}; + +// Models the server-side of the original input parameters. +// Provides IPC buffer validation and it is capable of reading the parameters +// out of the IPC buffer. +class CrossCallParamsEx : public CrossCallParams { + public: + // Factory constructor. Pass an IPCbuffer (and buffer size) that contains a + // pending IPCcall. This constructor will: + // 1) validate the IPC buffer. returns NULL is the IPCbuffer is malformed. + // 2) make a copy of the IPCbuffer (parameter capture) + static CrossCallParamsEx* CreateFromBuffer(void* buffer_base, + uint32 buffer_size, + uint32* output_size); + + // Provides IPCinput parameter raw access: + // index : the parameter to read; 0 is the first parameter + // returns NULL if the parameter is non-existent. If it exists it also + // returns the size in *size + void* GetRawParameter(uint32 index, uint32* size, ArgType* type); + + // Gets a parameter that is four bytes in size. + // Returns false if the parameter does not exist or is not 32 bits wide. + bool GetParameter32(uint32 index, uint32* param); + + // Gets a parameter that is void pointer in size. + // Returns false if the parameter does not exist or is not void pointer sized. + bool GetParameterVoidPtr(uint32 index, void** param); + + // Gets a parameter that is a string. Returns false if the parameter does not + // exist. + bool GetParameterStr(uint32 index, std::wstring* string); + + // Gets a parameter that is an in/out buffer. Returns false is the parameter + // does not exist or if the size of the actual parameter is not equal to the + // expected size. + bool GetParameterPtr(uint32 index, uint32 expected_size, void** pointer); + + // Frees the memory associated with the IPC parameters. + static void operator delete(void* raw_memory) throw(); + + private: + // Only the factory method CreateFromBuffer can construct these objects. + CrossCallParamsEx(); + + ParamInfo param_info_[1]; + DISALLOW_COPY_AND_ASSIGN(CrossCallParamsEx); +}; + +// Simple helper function that sets the members of CrossCallReturn +// to the proper state to signal a basic error. +void SetCallError(ResultCode error, CrossCallReturn* call_return); + +// Sets the internal status of call_return to signify the that IPC call +// completed successfully. +void SetCallSuccess(CrossCallReturn* call_return); + +// Represents the client process that initiated the IPC which boils down to the +// process handle and the job object handle that contains the client process. +struct ClientInfo { + HANDLE process; + HANDLE job_object; + DWORD process_id; +}; + +// All IPC-related information to be passed to the IPC handler. +struct IPCInfo { + int ipc_tag; + const ClientInfo* client_info; + CrossCallReturn return_info; +}; + +// This structure identifies IPC signatures. +struct IPCParams { + int ipc_tag; + ArgType args[kMaxIpcParams]; + + bool Matches(IPCParams* other) const { + return !memcmp(this, other, sizeof(*other)); + } +}; + +// Models an entity that can process an IPC message or it can route to another +// one that could handle it. When an IPC arrives the IPC implementation will: +// 1) call OnMessageReady() with the tag of the pending IPC. If the dispatcher +// returns NULL it means that it cannot handle this IPC but if it returns +// non-null, it must be the pointer to a dispatcher that can handle it. +// 2) When the IPC finally obtains a valid Dispatcher the IPC +// implementation creates a CrossCallParamsEx from the raw IPC buffer. +// 3) It calls the returned callback, with the IPC info and arguments. +class Dispatcher { + public: + // Called from the IPC implementation to handle a specific IPC message. + typedef bool (Dispatcher::*CallbackGeneric)(); + typedef bool (Dispatcher::*Callback0)(IPCInfo* ipc); + typedef bool (Dispatcher::*Callback1)(IPCInfo* ipc, void* p1); + typedef bool (Dispatcher::*Callback2)(IPCInfo* ipc, void* p1, void* p2); + typedef bool (Dispatcher::*Callback3)(IPCInfo* ipc, void* p1, void* p2, + void* p3); + typedef bool (Dispatcher::*Callback4)(IPCInfo* ipc, void* p1, void* p2, + void* p3, void* p4); + typedef bool (Dispatcher::*Callback5)(IPCInfo* ipc, void* p1, void* p2, + void* p3, void* p4, void* p5); + typedef bool (Dispatcher::*Callback6)(IPCInfo* ipc, void* p1, void* p2, + void* p3, void* p4, void* p5, void* p6); + typedef bool (Dispatcher::*Callback7)(IPCInfo* ipc, void* p1, void* p2, + void* p3, void* p4, void* p5, void* p6, + void* p7); + typedef bool (Dispatcher::*Callback8)(IPCInfo* ipc, void* p1, void* p2, + void* p3, void* p4, void* p5, void* p6, + void* p7, void* p8); + typedef bool (Dispatcher::*Callback9)(IPCInfo* ipc, void* p1, void* p2, + void* p3, void* p4, void* p5, void* p6, + void* p7, void* p8, void* p9); + + // Called from the IPC implementation when an IPC message is ready override + // on a derived class to handle a set of IPC messages. Return NULL if your + // subclass does not handle the message or return the pointer to the subclass + // that can handle it. + virtual Dispatcher* OnMessageReady(IPCParams* ipc, CallbackGeneric* callback); + + // Called when a target proces is created, to setup the interceptions related + // with the given service (IPC). + virtual bool SetupService(InterceptionManager* manager, int service) = 0; + + virtual ~Dispatcher() {} + + protected: + // Structure that defines an IPC Call with all the parameters and the handler. + struct IPCCall { + IPCParams params; + CallbackGeneric callback; + }; + + // List of IPC Calls supported by the class. + std::vector ipc_calls_; +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_CROSSCALL_SERVER_H_ diff --git a/sandbox/win/src/dep.cc b/sandbox/win/src/dep.cc new file mode 100644 index 0000000..4995601 --- /dev/null +++ b/sandbox/win/src/dep.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/dep.h" + +#include + +#include "base/logging.h" + +namespace sandbox { + +namespace { + +// These values are in the Windows 2008 SDK but not in the previous ones. Define +// the values here until we're sure everyone updated their SDK. +#ifndef PROCESS_DEP_ENABLE +#define PROCESS_DEP_ENABLE 0x00000001 +#endif +#ifndef PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION +#define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION 0x00000002 +#endif + +// SetProcessDEPPolicy is declared in the Windows 2008 SDK. +typedef BOOL (WINAPI *FnSetProcessDEPPolicy)(DWORD dwFlags); + +enum PROCESS_INFORMATION_CLASS { + ProcessExecuteFlags = 0x22, +}; + +// Flags named as per their usage. +const int MEM_EXECUTE_OPTION_ENABLE = 1; +const int MEM_EXECUTE_OPTION_DISABLE = 2; +const int MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION = 4; +const int MEM_EXECUTE_OPTION_PERMANENT = 8; + +// Not exactly the right signature but that will suffice. +typedef HRESULT (WINAPI *FnNtSetInformationProcess)( + HANDLE ProcessHandle, + PROCESS_INFORMATION_CLASS ProcessInformationClass, + PVOID ProcessInformation, + ULONG ProcessInformationLength); + +} // namespace + +bool SetCurrentProcessDEP(DepEnforcement enforcement) { +#ifdef _WIN64 + // DEP is always on in x64. + return enforcement != DEP_DISABLED; +#endif + // Only available on Windows XP SP2 and Windows Server 2003 SP1. + // For reference: http://www.uninformed.org/?v=2&a=4 + FnNtSetInformationProcess NtSetInformationProc = + reinterpret_cast( + GetProcAddress(GetModuleHandle(L"ntdll.dll"), + "NtSetInformationProcess")); + + if (!NtSetInformationProc) + return false; + + // Flags being used as per SetProcessDEPPolicy on Vista SP1. + ULONG dep_flags; + switch (enforcement) { + case DEP_DISABLED: + // 2 + dep_flags = MEM_EXECUTE_OPTION_DISABLE; + break; + case DEP_ENABLED: + // 9 + dep_flags = MEM_EXECUTE_OPTION_PERMANENT | MEM_EXECUTE_OPTION_ENABLE; + break; + case DEP_ENABLED_ATL7_COMPAT: + // 0xD + dep_flags = MEM_EXECUTE_OPTION_PERMANENT | MEM_EXECUTE_OPTION_ENABLE | + MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION; + break; + default: + NOTREACHED(); + return false; + } + + HRESULT status = NtSetInformationProc(GetCurrentProcess(), + ProcessExecuteFlags, + &dep_flags, + sizeof(dep_flags)); + return SUCCEEDED(status); +} + +} // namespace sandbox diff --git a/sandbox/win/src/dep.h b/sandbox/win/src/dep.h new file mode 100644 index 0000000..9016285 --- /dev/null +++ b/sandbox/win/src/dep.h @@ -0,0 +1,25 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_DEP_H__ +#define SANDBOX_SRC_DEP_H__ + +namespace sandbox { + +enum DepEnforcement { + // DEP is completely disabled. + DEP_DISABLED, + // DEP is permanently enforced. + DEP_ENABLED, + // DEP with support for ATL7 thunking is permanently enforced. + DEP_ENABLED_ATL7_COMPAT, +}; + +// Change the Data Execution Prevention (DEP) status for the current process. +// Once enabled, it cannot be disabled. +bool SetCurrentProcessDEP(DepEnforcement enforcement); + +} // namespace sandbox + +#endif // SANDBOX_SRC_DEP_H__ diff --git a/sandbox/win/src/dep_test.cc b/sandbox/win/src/dep_test.cc new file mode 100644 index 0000000..91d4e67 --- /dev/null +++ b/sandbox/win/src/dep_test.cc @@ -0,0 +1,158 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/dep.h" + +#include "sandbox/src/sandbox_utils.h" +#include "sandbox/tests/common/controller.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +namespace { + +BYTE kReturnCode[] = { + // ret + 0xC3, +}; + +typedef void (*NullFunction)(); + +// This doesn't fail on Vista Service Pack 0 but it does on XP SP2 and Vista +// SP1. I guess this is a bug in Vista SP0 w.r.t .data PE section. Needs +// investigation to be sure it is a bug and not an error on my part. +bool GenerateDepException() { + bool result = false; + __try { + void* code = kReturnCode; + // Call this code. + reinterpret_cast(code)(); + } __except(EXCEPTION_EXECUTE_HANDLER) { + result = true; + } + return result; +} + +bool GenerateDepAtl7Exception() { + // TODO(maruel): bug 1207762 Somehow test ATL7 + return GenerateDepException(); +} + +SBOX_TESTS_COMMAND int CheckDepLevel(int argc, wchar_t **argv) { + if (1 != argc) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + int flag = _wtoi(argv[0]); + switch (flag) { + case 1: + // DEP is completely disabled. + if (!SetCurrentProcessDEP(DEP_DISABLED)) { + if (!IsXPSP2OrLater()) + // That's fine. + return SBOX_TEST_SUCCEEDED; + return SBOX_TEST_DENIED; + } + if (GenerateDepException()) + return SBOX_TEST_FAILED; + if (GenerateDepAtl7Exception()) + return SBOX_TEST_FAILED; + return SBOX_TEST_SUCCEEDED; + case 2: + // DEP is enabled with ATL7 thunk support. + if (!SetCurrentProcessDEP(DEP_ENABLED_ATL7_COMPAT)) { + if (!IsXPSP2OrLater()) + // That's fine. + return SBOX_TEST_SUCCEEDED; + return SBOX_TEST_DENIED; + } + if (!GenerateDepException()) + return SBOX_TEST_FAILED; + if (GenerateDepAtl7Exception()) + return SBOX_TEST_FAILED; + return SBOX_TEST_SUCCEEDED; + case 3: + // DEP is enabled. + if (!SetCurrentProcessDEP(DEP_ENABLED)) { + if (!IsXPSP2OrLater()) + // That's fine. + return SBOX_TEST_SUCCEEDED; + return SBOX_TEST_DENIED; + } + if (!GenerateDepException()) + return SBOX_TEST_FAILED; + if (!GenerateDepAtl7Exception()) + return SBOX_TEST_FAILED; + return SBOX_TEST_SUCCEEDED; + case 4: + // DEP can't be disabled. + if (!SetCurrentProcessDEP(DEP_ENABLED)) { + if (!IsXPSP2OrLater()) + // That's fine. + return SBOX_TEST_SUCCEEDED; + } + if (SetCurrentProcessDEP(DEP_DISABLED)) { + return SBOX_TEST_DENIED; + } + // Verify that it is still enabled. + if (!GenerateDepException()) + return SBOX_TEST_FAILED; + if (!GenerateDepAtl7Exception()) + return SBOX_TEST_FAILED; + return SBOX_TEST_SUCCEEDED; + case 5: + // DEP can't be disabled. + if (!SetCurrentProcessDEP(DEP_ENABLED_ATL7_COMPAT)) { + if (!IsXPSP2OrLater()) + // That's fine. + return SBOX_TEST_SUCCEEDED; + } + if (SetCurrentProcessDEP(DEP_DISABLED)) { + return SBOX_TEST_DENIED; + } + // Verify that it is still enabled. + if (!GenerateDepException()) + return SBOX_TEST_FAILED; + if (!GenerateDepAtl7Exception()) + return SBOX_TEST_FAILED; + return SBOX_TEST_SUCCEEDED; + case 6: + // DEP can't be disabled. + if (!SetCurrentProcessDEP(DEP_ENABLED)) { + if (!IsXPSP2OrLater()) + // That's fine. + return SBOX_TEST_SUCCEEDED; + } + if (SetCurrentProcessDEP(DEP_ENABLED_ATL7_COMPAT)) { + return SBOX_TEST_DENIED; + } + // Verify that it is still enabled. + if (!GenerateDepException()) + return SBOX_TEST_FAILED; + if (!GenerateDepAtl7Exception()) + return SBOX_TEST_FAILED; + return SBOX_TEST_SUCCEEDED; + default: + return SBOX_TEST_INVALID_PARAMETER; + } + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; +} + +} // namespace + +// This test is disabled. See bug 1275842 +TEST(DepTest, DISABLED_TestDepDisable) { + TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE); + + runner.SetTimeout(INFINITE); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 1")); + // TODO(maruel): bug 1207762 Somehow test ATL7 + // EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 2")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 3")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 4")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 5")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDepLevel 6")); +} + +} // namespace sandbox diff --git a/sandbox/win/src/eat_resolver.cc b/sandbox/win/src/eat_resolver.cc new file mode 100644 index 0000000..f057006 --- /dev/null +++ b/sandbox/win/src/eat_resolver.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/eat_resolver.h" + +#include "base/win/pe_image.h" +#include "sandbox/src/sandbox_nt_util.h" + +namespace sandbox { + +NTSTATUS EatResolverThunk::Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used) { + NTSTATUS ret = Init(target_module, interceptor_module, target_name, + interceptor_name, interceptor_entry_point, + thunk_storage, storage_bytes); + if (!NT_SUCCESS(ret)) + return ret; + + if (!eat_entry_) + return STATUS_INVALID_PARAMETER; + + size_t thunk_bytes = GetInternalThunkSize(); + +#if defined(_WIN64) + // We have two thunks, in order: the return path and the forward path. + if (!SetInternalThunk(thunk_storage, storage_bytes, NULL, target_)) + return STATUS_BUFFER_TOO_SMALL; + + storage_bytes -= thunk_bytes; + thunk_storage = reinterpret_cast(thunk_storage) + thunk_bytes; +#endif + + if (!SetInternalThunk(thunk_storage, storage_bytes, target_, interceptor_)) + return STATUS_BUFFER_TOO_SMALL; + + AutoProtectMemory memory; + memory.ChangeProtection(eat_entry_, sizeof(DWORD), PAGE_READWRITE); + + // Perform the patch. +#pragma warning(push) +#pragma warning(disable: 4311) + // These casts generate warnings because they are 32 bit specific. + *eat_entry_ = reinterpret_cast(thunk_storage) - + reinterpret_cast(target_module); +#pragma warning(pop) + + if (NULL != storage_used) + *storage_used = GetThunkSize(); + + return ret; +} + +NTSTATUS EatResolverThunk::ResolveTarget(const void* module, + const char* function_name, + void** address) { + DCHECK_NT(address); + if (!module) + return STATUS_INVALID_PARAMETER; + + base::win::PEImage pe(module); + if (!pe.VerifyMagic()) + return STATUS_INVALID_IMAGE_FORMAT; + + eat_entry_ = pe.GetExportEntry(function_name); + + if (!eat_entry_) + return STATUS_PROCEDURE_NOT_FOUND; + + *address = pe.RVAToAddr(*eat_entry_); + + return STATUS_SUCCESS; +} + +size_t EatResolverThunk::GetThunkSize() const { +#if defined(_WIN64) + return GetInternalThunkSize() * 2; +#else + return GetInternalThunkSize(); +#endif +} + +} // namespace sandbox diff --git a/sandbox/win/src/eat_resolver.h b/sandbox/win/src/eat_resolver.h new file mode 100644 index 0000000..0d5b3e0 --- /dev/null +++ b/sandbox/win/src/eat_resolver.h @@ -0,0 +1,48 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_EAT_RESOLVER_H__ +#define SANDBOX_SRC_EAT_RESOLVER_H__ + +#include "base/basictypes.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/resolver.h" + +namespace sandbox { + +// This is the concrete resolver used to perform exports table interceptions. +class EatResolverThunk : public ResolverThunk { + public: + EatResolverThunk() : eat_entry_(NULL) {} + virtual ~EatResolverThunk() {} + + // Implementation of Resolver::Setup. + virtual NTSTATUS Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used); + + // Implementation of Resolver::ResolveTarget. + virtual NTSTATUS ResolveTarget(const void* module, + const char* function_name, + void** address); + + // Implementation of Resolver::GetThunkSize. + virtual size_t GetThunkSize() const; + + private: + // The entry to patch. + DWORD* eat_entry_; + + DISALLOW_COPY_AND_ASSIGN(EatResolverThunk); +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_EAT_RESOLVER_H__ diff --git a/sandbox/win/src/file_policy_test.cc b/sandbox/win/src/file_policy_test.cc new file mode 100644 index 0000000..df1e903 --- /dev/null +++ b/sandbox/win/src/file_policy_test.cc @@ -0,0 +1,598 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include +#include + +#include "base/win/scoped_handle.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/win_utils.h" +#include "sandbox/tests/common/controller.h" +#include "sandbox/tests/common/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define BINDNTDLL(name) \ + name ## Function name = reinterpret_cast( \ + ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) + +namespace sandbox { + +const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE; + +// Creates a file using different desired access. Returns if the call succeeded +// or not. The first argument in argv is the filename. If the second argument +// is "read", we try read only access. Otherwise we try read-write access. +SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t **argv) { + if (argc != 2) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + bool read = (_wcsicmp(argv[0], L"Read") == 0); + + if (read) { + base::win::ScopedHandle file1(CreateFile( + argv[1], GENERIC_READ, kSharing, NULL, OPEN_EXISTING, 0, NULL)); + base::win::ScopedHandle file2(CreateFile( + argv[1], FILE_EXECUTE, kSharing, NULL, OPEN_EXISTING, 0, NULL)); + + if (file1.Get() && file2.Get()) + return SBOX_TEST_SUCCEEDED; + return SBOX_TEST_DENIED; + } else { + base::win::ScopedHandle file1(CreateFile( + argv[1], GENERIC_ALL, kSharing, NULL, OPEN_EXISTING, 0, NULL)); + base::win::ScopedHandle file2(CreateFile( + argv[1], GENERIC_READ | FILE_WRITE_DATA, kSharing, NULL, OPEN_EXISTING, + 0, NULL)); + + if (file1.Get() && file2.Get()) + return SBOX_TEST_SUCCEEDED; + return SBOX_TEST_DENIED; + } +} + +SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) { + if (argc != 1) { + SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + + std::wstring full_path = MakePathToSys(argv[0], false); + if (full_path.empty()) { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + + HANDLE file = ::CreateFileW(full_path.c_str(), GENERIC_READ, kSharing, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (INVALID_HANDLE_VALUE != file) { + ::CloseHandle(file); + return SBOX_TEST_SUCCEEDED; + } else { + if (ERROR_ACCESS_DENIED == ::GetLastError()) { + return SBOX_TEST_DENIED; + } else { + return SBOX_TEST_FAILED; + } + } + return SBOX_TEST_SUCCEEDED; +} + +// Creates the file in parameter using the NtCreateFile api and returns if the +// call succeeded or not. +SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) { + BINDNTDLL(NtCreateFile); + BINDNTDLL(RtlInitUnicodeString); + if (!NtCreateFile || !RtlInitUnicodeString) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + if (argc != 1) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + std::wstring file(argv[0]); + if (0 != _wcsnicmp(file.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) + file = MakePathToSys(argv[0], true); + + UNICODE_STRING object_name; + RtlInitUnicodeString(&object_name, file.c_str()); + + OBJECT_ATTRIBUTES obj_attributes = {0}; + InitializeObjectAttributes(&obj_attributes, &object_name, + OBJ_CASE_INSENSITIVE, NULL, NULL); + + HANDLE handle; + IO_STATUS_BLOCK io_block = {0}; + NTSTATUS status = NtCreateFile(&handle, FILE_READ_DATA, &obj_attributes, + &io_block, NULL, 0, kSharing, FILE_OPEN, + 0, NULL, 0); + if (NT_SUCCESS(status)) { + ::CloseHandle(handle); + return SBOX_TEST_SUCCEEDED; + } else if (STATUS_ACCESS_DENIED == status) { + return SBOX_TEST_DENIED; + } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { + return SBOX_TEST_NOT_FOUND; + } + return SBOX_TEST_FAILED; +} + +// Opens the file in parameter using the NtOpenFile api and returns if the +// call succeeded or not. +SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { + BINDNTDLL(NtOpenFile); + BINDNTDLL(RtlInitUnicodeString); + if (!NtOpenFile || !RtlInitUnicodeString) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + if (argc != 1) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + std::wstring file = MakePathToSys(argv[0], true); + UNICODE_STRING object_name; + RtlInitUnicodeString(&object_name, file.c_str()); + + OBJECT_ATTRIBUTES obj_attributes = {0}; + InitializeObjectAttributes(&obj_attributes, &object_name, + OBJ_CASE_INSENSITIVE, NULL, NULL); + + HANDLE handle; + IO_STATUS_BLOCK io_block = {0}; + NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes, + &io_block, kSharing, 0); + if (NT_SUCCESS(status)) { + ::CloseHandle(handle); + return SBOX_TEST_SUCCEEDED; + } else if (STATUS_ACCESS_DENIED == status) { + return SBOX_TEST_DENIED; + } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { + return SBOX_TEST_NOT_FOUND; + } + return SBOX_TEST_FAILED; +} + +SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t **argv) { + std::wstring sys_path = MakePathToSys(L"", false); + if (sys_path.empty()) { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + ULARGE_INTEGER free_user = {0}; + ULARGE_INTEGER total = {0}; + ULARGE_INTEGER free_total = {0}; + if (::GetDiskFreeSpaceExW(sys_path.c_str(), &free_user, &total, + &free_total)) { + if ((total.QuadPart != 0) && (free_total.QuadPart !=0)) { + return SBOX_TEST_SUCCEEDED; + } + } else { + if (ERROR_ACCESS_DENIED == ::GetLastError()) { + return SBOX_TEST_DENIED; + } else { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + } + return SBOX_TEST_SUCCEEDED; +} + +// Move a file using the MoveFileEx api and returns if the call succeeded or +// not. +SBOX_TESTS_COMMAND int File_Rename(int argc, wchar_t **argv) { + if (argc != 2) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + if (::MoveFileEx(argv[0], argv[1], 0)) + return SBOX_TEST_SUCCEEDED; + + if (::GetLastError() != ERROR_ACCESS_DENIED) + return SBOX_TEST_FAILED; + + return SBOX_TEST_DENIED; +} + +// Query the attributes of file in parameter using the NtQueryAttributesFile api +// and NtQueryFullAttributesFile and returns if the call succeeded or not. The +// second argument in argv is "d" or "f" telling if we expect the attributes to +// specify a file or a directory. The expected attribute has to match the real +// attributes for the call to be successful. +SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t **argv) { + BINDNTDLL(NtQueryAttributesFile); + BINDNTDLL(NtQueryFullAttributesFile); + BINDNTDLL(RtlInitUnicodeString); + if (!NtQueryAttributesFile || !NtQueryFullAttributesFile || + !RtlInitUnicodeString) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + if (argc != 2) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + bool expect_directory = (L'd' == argv[1][0]); + + UNICODE_STRING object_name; + std::wstring file = MakePathToSys(argv[0], true); + RtlInitUnicodeString(&object_name, file.c_str()); + + OBJECT_ATTRIBUTES obj_attributes = {0}; + InitializeObjectAttributes(&obj_attributes, &object_name, + OBJ_CASE_INSENSITIVE, NULL, NULL); + + FILE_BASIC_INFORMATION info = {0}; + FILE_NETWORK_OPEN_INFORMATION full_info = {0}; + NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info); + NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info); + + if (status1 != status2) + return SBOX_TEST_FAILED; + + if (NT_SUCCESS(status1)) { + if (info.FileAttributes != full_info.FileAttributes) + return SBOX_TEST_FAILED; + + bool is_directory1 = (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + if (expect_directory == is_directory1) + return SBOX_TEST_SUCCEEDED; + } else if (STATUS_ACCESS_DENIED == status1) { + return SBOX_TEST_DENIED; + } else if (STATUS_OBJECT_NAME_NOT_FOUND == status1) { + return SBOX_TEST_NOT_FOUND; + } + + return SBOX_TEST_FAILED; +} + +TEST(FilePolicyTest, DenyNtCreateCalc) { + TestRunner runner; + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, + L"calc.exe")); + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 calc.exe")); + + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); +} + +TEST(FilePolicyTest, AllowNtCreateCalc) { + TestRunner runner; + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); + + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); +} + +TEST(FilePolicyTest, AllowNtCreateWithNativePath) { + std::wstring calc = MakePathToSys(L"calc.exe", false); + std::wstring nt_path; + ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path)); + TestRunner runner; + runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str()); + + wchar_t buff[MAX_PATH]; + ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff)); + + std::transform(nt_path.begin(), nt_path.end(), nt_path.begin(), std::tolower); + ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff)); +} + +TEST(FilePolicyTest, AllowReadOnly) { + TestRunner runner; + + // Create a temp file because we need write access to it. + wchar_t temp_directory[MAX_PATH]; + wchar_t temp_file_name[MAX_PATH]; + ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); + + EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, + temp_file_name)); + + wchar_t command_read[MAX_PATH + 20] = {0}; + wsprintf(command_read, L"File_Create Read \"%ls\"", temp_file_name); + wchar_t command_write[MAX_PATH + 20] = {0}; + wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name); + + // Verify that we have read access after revert. + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_read)); + + // Verify that we don't have write access after revert. + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write)); + + // Verify that we really have write access to the file. + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write)); + + DeleteFile(temp_file_name); +} + +TEST(FilePolicyTest, AllowWildcard) { + TestRunner runner; + + // Create a temp file because we need write access to it. + wchar_t temp_directory[MAX_PATH]; + wchar_t temp_file_name[MAX_PATH]; + ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); + + wcscat_s(temp_directory, MAX_PATH, L"*"); + EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_directory)); + + wchar_t command_write[MAX_PATH + 20] = {0}; + wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name); + + // Verify that we have write access after revert. + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write)); + + DeleteFile(temp_file_name); +} + +TEST(FilePolicyTest, AllowNtCreatePatternRule) { + TestRunner runner; + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"App*.dll")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"File_OpenSys32 appmgmts.dll")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_OpenSys32 appwiz.cpl")); + + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"File_OpenSys32 appmgmts.dll")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_OpenSys32 appwiz.cpl")); +} + +TEST(FilePolicyTest, CheckNotFound) { + TestRunner runner; + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"n*.dll")); + + EXPECT_EQ(SBOX_TEST_NOT_FOUND, + runner.RunTest(L"File_OpenSys32 notfound.dll")); +} + +TEST(FilePolicyTest, CheckNoLeak) { + TestRunner runner; + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 notfound.exe")); +} + +TEST(FilePolicyTest, TestQueryAttributesFile) { + TestRunner runner; + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, + L"appmgmts.dll")); + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, + L"notfound.exe")); + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"drivers")); + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_QUERY, + L"ipconfig.exe")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"File_QueryAttributes drivers d")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"File_QueryAttributes appmgmts.dll f")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"File_QueryAttributes ipconfig.exe f")); + + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"File_QueryAttributes ftp.exe f")); + + EXPECT_EQ(SBOX_TEST_NOT_FOUND, + runner.RunTest(L"File_QueryAttributes notfound.exe f")); +} + +// Makes sure that we don't leak information when there is not policy to allow +// a path. +TEST(FilePolicyTest, TestQueryAttributesFileNoPolicy) { + TestRunner runner; + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"File_QueryAttributes ftp.exe f")); + + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"File_QueryAttributes notfound.exe f")); +} + +TEST(FilePolicyTest, TestRename) { + TestRunner runner; + + // Give access to the temp directory. + wchar_t temp_directory[MAX_PATH]; + wchar_t temp_file_name1[MAX_PATH]; + wchar_t temp_file_name2[MAX_PATH]; + wchar_t temp_file_name3[MAX_PATH]; + wchar_t temp_file_name4[MAX_PATH]; + wchar_t temp_file_name5[MAX_PATH]; + wchar_t temp_file_name6[MAX_PATH]; + wchar_t temp_file_name7[MAX_PATH]; + wchar_t temp_file_name8[MAX_PATH]; + ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name1), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name2), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name3), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name4), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name5), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name6), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name7), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name8), 0u); + + + // Add rules to make file1->file2 succeed. + ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name1)); + ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name2)); + + // Add rules to make file3->file4 fail. + ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name3)); + ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, + temp_file_name4)); + + // Add rules to make file5->file6 fail. + ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, + temp_file_name5)); + ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name6)); + + // Add rules to make file7->no_pol_file fail. + ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name7)); + + // Delete the files where the files are going to be renamed to. + ::DeleteFile(temp_file_name2); + ::DeleteFile(temp_file_name4); + ::DeleteFile(temp_file_name6); + ::DeleteFile(temp_file_name8); + + + wchar_t command[MAX_PATH*2 + 20] = {0}; + wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name1, + temp_file_name2); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command)); + + wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name3, + temp_file_name4); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); + + wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name5, + temp_file_name6); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); + + wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name7, + temp_file_name8); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); + + + // Delete all the files in case they are still there. + ::DeleteFile(temp_file_name1); + ::DeleteFile(temp_file_name2); + ::DeleteFile(temp_file_name3); + ::DeleteFile(temp_file_name4); + ::DeleteFile(temp_file_name5); + ::DeleteFile(temp_file_name6); + ::DeleteFile(temp_file_name7); + ::DeleteFile(temp_file_name8); +} + +TEST(FilePolicyTest, OpenSys32FilesDenyBecauseOfDir) { + TestRunner runner; + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, + L"notepad.exe")); + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe")); + + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"File_Win32Create notepad.exe")); +} + +TEST(FilePolicyTest, OpenSys32FilesAllowNotepad) { + TestRunner runner; + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, + L"notepad.exe")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"File_Win32Create notepad.exe")); + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create calc.exe")); + + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"File_Win32Create notepad.exe")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_Win32Create calc.exe")); +} + +TEST(FilePolicyTest, FileGetDiskSpace) { + TestRunner runner; + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_GetDiskSpace")); + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace")); + + // Add an 'allow' rule in the windows\system32 such that GetDiskFreeSpaceEx + // succeeds (it does an NtOpenFile) but windows\system32\notepad.exe is + // denied since there is no wild card in the rule. + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"")); + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace")); + + runner.SetTestState(AFTER_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe")); +} + +TEST(FilePolicyTest, TestReparsePoint) { + TestRunner runner; + + // Create a temp file because we need write access to it. + wchar_t temp_directory[MAX_PATH]; + wchar_t temp_file_name[MAX_PATH]; + ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); + + // Delete the file and create a directory instead. + ASSERT_TRUE(::DeleteFile(temp_file_name)); + ASSERT_TRUE(::CreateDirectory(temp_file_name, NULL)); + + // Create a temporary file in the subfolder. + std::wstring subfolder = temp_file_name; + std::wstring temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1); + std::wstring temp_file = subfolder + L"\\file_" + temp_file_title; + + HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + CREATE_ALWAYS, 0, NULL); + ASSERT_TRUE(INVALID_HANDLE_VALUE != file); + ASSERT_TRUE(::CloseHandle(file)); + + // Create a temporary file in the temp directory. + std::wstring temp_dir = temp_directory; + std::wstring temp_file_in_temp = temp_dir + L"file_" + temp_file_title; + file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + CREATE_ALWAYS, 0, NULL); + ASSERT_TRUE(file != NULL); + ASSERT_TRUE(::CloseHandle(file)); + + // Give write access to the temp directory. + std::wstring temp_dir_wildcard = temp_dir + L"*"; + EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, + temp_dir_wildcard.c_str())); + + // Prepare the command to execute. + std::wstring command_write; + command_write += L"File_Create Write \""; + command_write += temp_file; + command_write += L"\""; + + // Verify that we have write access to the original file + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str())); + + // Replace the subfolder by a reparse point to %temp%. + ::DeleteFile(temp_file.c_str()); + HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); + + std::wstring temp_dir_nt; + temp_dir_nt += L"\\??\\"; + temp_dir_nt += temp_dir; + EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); + EXPECT_TRUE(::CloseHandle(dir)); + + // Try to open the file again. + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str())); + + // Remove the reparse point. + dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); + EXPECT_TRUE(DeleteReparsePoint(dir)); + EXPECT_TRUE(::CloseHandle(dir)); + + // Cleanup. + EXPECT_TRUE(::DeleteFile(temp_file_in_temp.c_str())); + EXPECT_TRUE(::RemoveDirectory(subfolder.c_str())); +} + +} // namespace sandbox diff --git a/sandbox/win/src/filesystem_dispatcher.cc b/sandbox/win/src/filesystem_dispatcher.cc new file mode 100644 index 0000000..71a6f02 --- /dev/null +++ b/sandbox/win/src/filesystem_dispatcher.cc @@ -0,0 +1,297 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/filesystem_dispatcher.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/filesystem_interception.h" +#include "sandbox/src/filesystem_policy.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_broker.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_nt_util.h" + +namespace sandbox { + +FilesystemDispatcher::FilesystemDispatcher(PolicyBase* policy_base) + : policy_base_(policy_base) { + static const IPCCall create_params = { + {IPC_NTCREATEFILE_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE, + ULONG_TYPE, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast(&FilesystemDispatcher::NtCreateFile) + }; + + static const IPCCall open_file = { + {IPC_NTOPENFILE_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE, + ULONG_TYPE}, + reinterpret_cast(&FilesystemDispatcher::NtOpenFile) + }; + + static const IPCCall attribs = { + {IPC_NTQUERYATTRIBUTESFILE_TAG, WCHAR_TYPE, ULONG_TYPE, INOUTPTR_TYPE}, + reinterpret_cast( + &FilesystemDispatcher::NtQueryAttributesFile) + }; + + static const IPCCall full_attribs = { + {IPC_NTQUERYFULLATTRIBUTESFILE_TAG, WCHAR_TYPE, ULONG_TYPE, INOUTPTR_TYPE}, + reinterpret_cast( + &FilesystemDispatcher::NtQueryFullAttributesFile) + }; + + static const IPCCall set_info = { + {IPC_NTSETINFO_RENAME_TAG, VOIDPTR_TYPE, INOUTPTR_TYPE, INOUTPTR_TYPE, + ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast( + &FilesystemDispatcher::NtSetInformationFile) + }; + + ipc_calls_.push_back(create_params); + ipc_calls_.push_back(open_file); + ipc_calls_.push_back(attribs); + ipc_calls_.push_back(full_attribs); + ipc_calls_.push_back(set_info); +} + +bool FilesystemDispatcher::SetupService(InterceptionManager* manager, + int service) { + switch (service) { + case IPC_NTCREATEFILE_TAG: + return INTERCEPT_NT(manager, NtCreateFile, CREATE_FILE_ID, 48); + + case IPC_NTOPENFILE_TAG: + return INTERCEPT_NT(manager, NtOpenFile, OPEN_FILE_ID, 28); + + case IPC_NTQUERYATTRIBUTESFILE_TAG: + return INTERCEPT_NT(manager, NtQueryAttributesFile, QUERY_ATTRIB_FILE_ID, + 12); + + case IPC_NTQUERYFULLATTRIBUTESFILE_TAG: + return INTERCEPT_NT(manager, NtQueryFullAttributesFile, + QUERY_FULL_ATTRIB_FILE_ID, 12); + + case IPC_NTSETINFO_RENAME_TAG: + return INTERCEPT_NT(manager, NtSetInformationFile, SET_INFO_FILE_ID, 24); + + default: + return false; + } +} + +bool FilesystemDispatcher::NtCreateFile( + IPCInfo* ipc, std::wstring* name, DWORD attributes, DWORD desired_access, + DWORD file_attributes, DWORD share_access, DWORD create_disposition, + DWORD create_options) { + if (!PreProcessName(*name, name)) { + // The path requested might contain a reparse point. + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + const wchar_t* filename = name->c_str(); + + ULONG broker = TRUE; + CountedParameterSet params; + params[OpenFile::NAME] = ParamPickerMake(filename); + params[OpenFile::ACCESS] = ParamPickerMake(desired_access); + params[OpenFile::OPTIONS] = ParamPickerMake(create_options); + params[OpenFile::BROKER] = ParamPickerMake(broker); + + // To evaluate the policy we need to call back to the policy object. We + // are just middlemen in the operation since is the FileSystemPolicy which + // knows what to do. + EvalResult result = policy_base_->EvalPolicy(IPC_NTCREATEFILE_TAG, + params.GetBase()); + HANDLE handle; + ULONG_PTR io_information = 0; + NTSTATUS nt_status; + if (!FileSystemPolicy::CreateFileAction(result, *ipc->client_info, *name, + attributes, desired_access, + file_attributes, share_access, + create_disposition, create_options, + &handle, &nt_status, + &io_information)) { + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + // Return operation status on the IPC. + ipc->return_info.extended[0].ulong_ptr = io_information; + ipc->return_info.nt_status = nt_status; + ipc->return_info.handle = handle; + return true; +} + +bool FilesystemDispatcher::NtOpenFile( + IPCInfo* ipc, std::wstring* name, DWORD attributes, DWORD desired_access, + DWORD share_access, DWORD open_options) { + if (!PreProcessName(*name, name)) { + // The path requested might contain a reparse point. + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + const wchar_t* filename = name->c_str(); + + ULONG broker = TRUE; + CountedParameterSet params; + params[OpenFile::NAME] = ParamPickerMake(filename); + params[OpenFile::ACCESS] = ParamPickerMake(desired_access); + params[OpenFile::OPTIONS] = ParamPickerMake(open_options); + params[OpenFile::BROKER] = ParamPickerMake(broker); + + // To evaluate the policy we need to call back to the policy object. We + // are just middlemen in the operation since is the FileSystemPolicy which + // knows what to do. + EvalResult result = policy_base_->EvalPolicy(IPC_NTOPENFILE_TAG, + params.GetBase()); + HANDLE handle; + ULONG_PTR io_information = 0; + NTSTATUS nt_status; + if (!FileSystemPolicy::OpenFileAction(result, *ipc->client_info, *name, + attributes, desired_access, + share_access, open_options, &handle, + &nt_status, &io_information)) { + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + // Return operation status on the IPC. + ipc->return_info.extended[0].ulong_ptr = io_information; + ipc->return_info.nt_status = nt_status; + ipc->return_info.handle = handle; + return true; +} + +bool FilesystemDispatcher::NtQueryAttributesFile( + IPCInfo* ipc, std::wstring* name, DWORD attributes, CountedBuffer* info) { + if (sizeof(FILE_BASIC_INFORMATION) != info->Size()) + return false; + + if (!PreProcessName(*name, name)) { + // The path requested might contain a reparse point. + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + ULONG broker = TRUE; + const wchar_t* filename = name->c_str(); + CountedParameterSet params; + params[FileName::NAME] = ParamPickerMake(filename); + params[FileName::BROKER] = ParamPickerMake(broker); + + // To evaluate the policy we need to call back to the policy object. We + // are just middlemen in the operation since is the FileSystemPolicy which + // knows what to do. + EvalResult result = policy_base_->EvalPolicy(IPC_NTQUERYATTRIBUTESFILE_TAG, + params.GetBase()); + + FILE_BASIC_INFORMATION* information = + reinterpret_cast(info->Buffer()); + NTSTATUS nt_status; + if (!FileSystemPolicy::QueryAttributesFileAction(result, *ipc->client_info, + *name, attributes, + information, &nt_status)) { + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + // Return operation status on the IPC. + ipc->return_info.nt_status = nt_status; + return true; +} + +bool FilesystemDispatcher::NtQueryFullAttributesFile( + IPCInfo* ipc, std::wstring* name, DWORD attributes, CountedBuffer* info) { + if (sizeof(FILE_NETWORK_OPEN_INFORMATION) != info->Size()) + return false; + + if (!PreProcessName(*name, name)) { + // The path requested might contain a reparse point. + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + ULONG broker = TRUE; + const wchar_t* filename = name->c_str(); + CountedParameterSet params; + params[FileName::NAME] = ParamPickerMake(filename); + params[FileName::BROKER] = ParamPickerMake(broker); + + // To evaluate the policy we need to call back to the policy object. We + // are just middlemen in the operation since is the FileSystemPolicy which + // knows what to do. + EvalResult result = policy_base_->EvalPolicy( + IPC_NTQUERYFULLATTRIBUTESFILE_TAG, params.GetBase()); + + FILE_NETWORK_OPEN_INFORMATION* information = + reinterpret_cast(info->Buffer()); + NTSTATUS nt_status; + if (!FileSystemPolicy::QueryFullAttributesFileAction(result, + *ipc->client_info, + *name, attributes, + information, + &nt_status)) { + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + // Return operation status on the IPC. + ipc->return_info.nt_status = nt_status; + return true; +} + +bool FilesystemDispatcher::NtSetInformationFile( + IPCInfo* ipc, HANDLE handle, CountedBuffer* status, CountedBuffer* info, + DWORD length, DWORD info_class) { + if (sizeof(IO_STATUS_BLOCK) != status->Size()) + return false; + if (length != info->Size()) + return false; + + FILE_RENAME_INFORMATION* rename_info = + reinterpret_cast(info->Buffer()); + + if (!IsSupportedRenameCall(rename_info, length, info_class)) + return false; + + std::wstring name; + name.assign(rename_info->FileName, rename_info->FileNameLength / + sizeof(rename_info->FileName[0])); + if (!PreProcessName(name, &name)) { + // The path requested might contain a reparse point. + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + ULONG broker = TRUE; + const wchar_t* filename = name.c_str(); + CountedParameterSet params; + params[FileName::NAME] = ParamPickerMake(filename); + params[FileName::BROKER] = ParamPickerMake(broker); + + // To evaluate the policy we need to call back to the policy object. We + // are just middlemen in the operation since is the FileSystemPolicy which + // knows what to do. + EvalResult result = policy_base_->EvalPolicy(IPC_NTSETINFO_RENAME_TAG, + params.GetBase()); + + IO_STATUS_BLOCK* io_status = + reinterpret_cast(status->Buffer()); + NTSTATUS nt_status; + if (!FileSystemPolicy::SetInformationFileAction(result, *ipc->client_info, + handle, rename_info, length, + info_class, io_status, + &nt_status)) { + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + // Return operation status on the IPC. + ipc->return_info.nt_status = nt_status; + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/filesystem_dispatcher.h b/sandbox/win/src/filesystem_dispatcher.h new file mode 100644 index 0000000..d828715 --- /dev/null +++ b/sandbox/win/src/filesystem_dispatcher.h @@ -0,0 +1,57 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__ +#define SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__ + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sandbox_policy_base.h" + +namespace sandbox { + +// This class handles file system-related IPC calls. +class FilesystemDispatcher : public Dispatcher { + public: + explicit FilesystemDispatcher(PolicyBase* policy_base); + ~FilesystemDispatcher() {} + + // Dispatcher interface. + virtual bool SetupService(InterceptionManager* manager, int service); + + private: + // Processes IPC requests coming from calls to NtCreateFile in the target. + bool NtCreateFile(IPCInfo* ipc, std::wstring* name, DWORD attributes, + DWORD desired_access, DWORD file_attributes, + DWORD share_access, DWORD create_disposition, + DWORD create_options); + + // Processes IPC requests coming from calls to NtOpenFile in the target. + bool NtOpenFile(IPCInfo* ipc, std::wstring* name, DWORD attributes, + DWORD desired_access, DWORD share_access, + DWORD create_options); + + // Processes IPC requests coming from calls to NtQueryAttributesFile in the + // target. + bool NtQueryAttributesFile(IPCInfo* ipc, std::wstring* name, DWORD attributes, + CountedBuffer* info); + + // Processes IPC requests coming from calls to NtQueryFullAttributesFile in + // the target. + bool NtQueryFullAttributesFile(IPCInfo* ipc, std::wstring* name, + DWORD attributes, CountedBuffer* info); + + // Processes IPC requests coming from calls to NtSetInformationFile with the + // rename information class. + bool NtSetInformationFile(IPCInfo* ipc, HANDLE handle, CountedBuffer* status, + CountedBuffer* info, DWORD length, + DWORD info_class); + + PolicyBase* policy_base_; + DISALLOW_COPY_AND_ASSIGN(FilesystemDispatcher); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__ diff --git a/sandbox/win/src/filesystem_interception.cc b/sandbox/win/src/filesystem_interception.cc new file mode 100644 index 0000000..cdc10ff --- /dev/null +++ b/sandbox/win/src/filesystem_interception.cc @@ -0,0 +1,351 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/filesystem_interception.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/policy_target.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile, + PHANDLE file, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + PIO_STATUS_BLOCK io_status, + PLARGE_INTEGER allocation_size, + ULONG file_attributes, ULONG sharing, + ULONG disposition, ULONG options, + PVOID ea_buffer, ULONG ea_length) { + // Check if the process can open it first. + NTSTATUS status = orig_CreateFile(file, desired_access, object_attributes, + io_status, allocation_size, + file_attributes, sharing, disposition, + options, ea_buffer, ea_length); + if (STATUS_ACCESS_DENIED != status) + return status; + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return status; + + do { + if (!ValidParameter(file, sizeof(HANDLE), WRITE)) + break; + if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + wchar_t* name; + uint32 attributes = 0; + NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, + NULL); + if (!NT_SUCCESS(ret) || NULL == name) + break; + + ULONG broker = FALSE; + CountedParameterSet params; + params[OpenFile::NAME] = ParamPickerMake(name); + params[OpenFile::ACCESS] = ParamPickerMake(desired_access); + params[OpenFile::OPTIONS] = ParamPickerMake(options); + params[OpenFile::BROKER] = ParamPickerMake(broker); + + if (!QueryBroker(IPC_NTCREATEFILE_TAG, params.GetBase())) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + // The following call must match in the parameters with + // FilesystemDispatcher::ProcessNtCreateFile. + ResultCode code = CrossCall(ipc, IPC_NTCREATEFILE_TAG, name, attributes, + desired_access, file_attributes, sharing, + disposition, options, &answer); + + operator delete(name, NT_ALLOC); + + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + return answer.nt_status; + + __try { + *file = answer.handle; + io_status->Status = answer.nt_status; + io_status->Information = answer.extended[0].ulong_ptr; + status = io_status->Status; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + } while (false); + + return status; +} + +NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile, PHANDLE file, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + PIO_STATUS_BLOCK io_status, ULONG sharing, + ULONG options) { + // Check if the process can open it first. + NTSTATUS status = orig_OpenFile(file, desired_access, object_attributes, + io_status, sharing, options); + if (STATUS_ACCESS_DENIED != status) + return status; + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return status; + + do { + if (!ValidParameter(file, sizeof(HANDLE), WRITE)) + break; + if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + wchar_t* name; + uint32 attributes; + NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, + NULL); + if (!NT_SUCCESS(ret) || NULL == name) + break; + + ULONG broker = FALSE; + CountedParameterSet params; + params[OpenFile::NAME] = ParamPickerMake(name); + params[OpenFile::ACCESS] = ParamPickerMake(desired_access); + params[OpenFile::OPTIONS] = ParamPickerMake(options); + params[OpenFile::BROKER] = ParamPickerMake(broker); + + if (!QueryBroker(IPC_NTOPENFILE_TAG, params.GetBase())) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_NTOPENFILE_TAG, name, attributes, + desired_access, sharing, options, &answer); + + operator delete(name, NT_ALLOC); + + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + return answer.nt_status; + + __try { + *file = answer.handle; + io_status->Status = answer.nt_status; + io_status->Information = answer.extended[0].ulong_ptr; + status = io_status->Status; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + } while (false); + + return status; +} + +NTSTATUS WINAPI TargetNtQueryAttributesFile( + NtQueryAttributesFileFunction orig_QueryAttributes, + POBJECT_ATTRIBUTES object_attributes, + PFILE_BASIC_INFORMATION file_attributes) { + // Check if the process can query it first. + NTSTATUS status = orig_QueryAttributes(object_attributes, file_attributes); + if (STATUS_ACCESS_DENIED != status) + return status; + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return status; + + do { + if (!ValidParameter(file_attributes, sizeof(FILE_BASIC_INFORMATION), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + wchar_t* name = NULL; + uint32 attributes = 0; + NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, + NULL); + if (!NT_SUCCESS(ret) || NULL == name) + break; + + InOutCountedBuffer file_info(file_attributes, + sizeof(FILE_BASIC_INFORMATION)); + + ULONG broker = FALSE; + CountedParameterSet params; + params[FileName::NAME] = ParamPickerMake(name); + params[FileName::BROKER] = ParamPickerMake(broker); + + if (!QueryBroker(IPC_NTQUERYATTRIBUTESFILE_TAG, params.GetBase())) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_NTQUERYATTRIBUTESFILE_TAG, name, + attributes, file_info, &answer); + + operator delete(name, NT_ALLOC); + + if (SBOX_ALL_OK != code) + break; + + return answer.nt_status; + + } while (false); + + return status; +} + +NTSTATUS WINAPI TargetNtQueryFullAttributesFile( + NtQueryFullAttributesFileFunction orig_QueryFullAttributes, + POBJECT_ATTRIBUTES object_attributes, + PFILE_NETWORK_OPEN_INFORMATION file_attributes) { + // Check if the process can query it first. + NTSTATUS status = orig_QueryFullAttributes(object_attributes, + file_attributes); + if (STATUS_ACCESS_DENIED != status) + return status; + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return status; + + do { + if (!ValidParameter(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION), + WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + wchar_t* name = NULL; + uint32 attributes = 0; + NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, + NULL); + if (!NT_SUCCESS(ret) || NULL == name) + break; + + InOutCountedBuffer file_info(file_attributes, + sizeof(FILE_NETWORK_OPEN_INFORMATION)); + + ULONG broker = FALSE; + CountedParameterSet params; + params[FileName::NAME] = ParamPickerMake(name); + params[FileName::BROKER] = ParamPickerMake(broker); + + if (!QueryBroker(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, params.GetBase())) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_NTQUERYFULLATTRIBUTESFILE_TAG, name, + attributes, file_info, &answer); + + operator delete(name, NT_ALLOC); + + if (SBOX_ALL_OK != code) + break; + + return answer.nt_status; + } while (false); + + return status; +} + +NTSTATUS WINAPI TargetNtSetInformationFile( + NtSetInformationFileFunction orig_SetInformationFile, HANDLE file, + PIO_STATUS_BLOCK io_status, PVOID file_info, ULONG length, + FILE_INFORMATION_CLASS file_info_class) { + // Check if the process can open it first. + NTSTATUS status = orig_SetInformationFile(file, io_status, file_info, length, + file_info_class); + if (STATUS_ACCESS_DENIED != status) + return status; + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return status; + + do { + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE)) + break; + + if (!ValidParameter(file_info, length, READ)) + break; + + FILE_RENAME_INFORMATION* file_rename_info = + reinterpret_cast(file_info); + OBJECT_ATTRIBUTES object_attributes; + UNICODE_STRING object_name; + InitializeObjectAttributes(&object_attributes, &object_name, 0, NULL, NULL); + + __try { + if (!IsSupportedRenameCall(file_rename_info, length, file_info_class)) + break; + + object_attributes.RootDirectory = file_rename_info->RootDirectory; + object_name.Buffer = file_rename_info->FileName; + object_name.Length = object_name.MaximumLength = + static_cast(file_rename_info->FileNameLength); + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + + wchar_t* name; + NTSTATUS ret = AllocAndCopyName(&object_attributes, &name, NULL, NULL); + if (!NT_SUCCESS(ret) || !name) + break; + + ULONG broker = FALSE; + CountedParameterSet params; + params[FileName::NAME] = ParamPickerMake(name); + params[FileName::BROKER] = ParamPickerMake(broker); + + if (!QueryBroker(IPC_NTSETINFO_RENAME_TAG, params.GetBase())) + break; + + InOutCountedBuffer io_status_buffer(io_status, sizeof(IO_STATUS_BLOCK)); + // This is actually not an InOut buffer, only In, but using InOut facility + // really helps to simplify the code. + InOutCountedBuffer file_info_buffer(file_info, length); + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_NTSETINFO_RENAME_TAG, file, + io_status_buffer, file_info_buffer, length, + file_info_class, &answer); + + if (SBOX_ALL_OK != code) + break; + + status = answer.nt_status; + } while (false); + + return status; +} + +} // namespace sandbox diff --git a/sandbox/win/src/filesystem_interception.h b/sandbox/win/src/filesystem_interception.h new file mode 100644 index 0000000..d8d9e58 --- /dev/null +++ b/sandbox/win/src/filesystem_interception.h @@ -0,0 +1,53 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +#ifndef SANDBOX_SRC_FILESYSTEM_INTERCEPTION_H__ +#define SANDBOX_SRC_FILESYSTEM_INTERCEPTION_H__ + +namespace sandbox { + +extern "C" { + +// Interception of NtCreateFile on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateFile( + NtCreateFileFunction orig_CreateFile, PHANDLE file, + ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes, + PIO_STATUS_BLOCK io_status, PLARGE_INTEGER allocation_size, + ULONG file_attributes, ULONG sharing, ULONG disposition, ULONG options, + PVOID ea_buffer, ULONG ea_length); + +// Interception of NtOpenFile on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenFile( + NtOpenFileFunction orig_OpenFile, PHANDLE file, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, + ULONG sharing, ULONG options); + +// Interception of NtQueryAtttributesFile on the child process. +// It should never be called directly. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryAttributesFile( + NtQueryAttributesFileFunction orig_QueryAttributes, + POBJECT_ATTRIBUTES object_attributes, + PFILE_BASIC_INFORMATION file_attributes); + +// Interception of NtQueryFullAtttributesFile on the child process. +// It should never be called directly. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile( + NtQueryFullAttributesFileFunction orig_QueryAttributes, + POBJECT_ATTRIBUTES object_attributes, + PFILE_NETWORK_OPEN_INFORMATION file_attributes); + +// Interception of NtSetInformationFile on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationFile( + NtSetInformationFileFunction orig_SetInformationFile, HANDLE file, + PIO_STATUS_BLOCK io_status, PVOID file_information, ULONG length, + FILE_INFORMATION_CLASS file_information_class); + +} // extern "C" + +} // namespace sandbox + +#endif // SANDBOX_SRC_FILESYSTEM_INTERCEPTION_H__ diff --git a/sandbox/win/src/filesystem_policy.cc b/sandbox/win/src/filesystem_policy.cc new file mode 100644 index 0000000..385f4ae --- /dev/null +++ b/sandbox/win/src/filesystem_policy.cc @@ -0,0 +1,387 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "sandbox/src/filesystem_policy.h" + +#include "base/logging.h" +#include "base/win/scoped_handle.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_engine_opcodes.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox_utils.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/win_utils.h" + +namespace { + +NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle, + ACCESS_MASK desired_access, + OBJECT_ATTRIBUTES* obj_attributes, + IO_STATUS_BLOCK* io_status_block, + ULONG file_attributes, + ULONG share_access, + ULONG create_disposition, + ULONG create_options, + PVOID ea_buffer, + ULONG ea_lenght, + HANDLE target_process) { + NtCreateFileFunction NtCreateFile = NULL; + ResolveNTFunctionPtr("NtCreateFile", &NtCreateFile); + + HANDLE local_handle = INVALID_HANDLE_VALUE; + NTSTATUS status = NtCreateFile(&local_handle, desired_access, obj_attributes, + io_status_block, NULL, file_attributes, + share_access, create_disposition, + create_options, ea_buffer, ea_lenght); + if (!NT_SUCCESS(status)) { + return status; + } + + if (!sandbox::SameObject(local_handle, obj_attributes->ObjectName->Buffer)) { + // The handle points somewhere else. Fail the operation. + ::CloseHandle(local_handle); + return STATUS_ACCESS_DENIED; + } + + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, + target_process, target_file_handle, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(local_handle); + return STATUS_ACCESS_DENIED; + } + return STATUS_SUCCESS; +} + +} // namespace. + +namespace sandbox { + +bool FileSystemPolicy::GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy) { + std::wstring mod_name(name); + if (mod_name.empty()) { + return false; + } + + // Don't do any pre-processing if the name starts like the the native + // object manager style. + if (0 != _wcsnicmp(mod_name.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) { + // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the + // infrastructure to normalize names. In any case we need to escape the + // question marks. + if (!PreProcessName(mod_name, &mod_name)) { + // The path to be added might contain a reparse point. + NOTREACHED(); + return false; + } + if (0 != mod_name.compare(0, kNTPrefixLen, kNTPrefix)) { + // TODO(nsylvain): Find a better way to do name resolution. Right now we + // take the name and we expand it. + mod_name.insert(0, L"\\/?/?\\"); + name = mod_name.c_str(); + } + } + + EvalResult result = ASK_BROKER; + + // List of supported calls for the filesystem. + const unsigned kCallNtCreateFile = 0x1; + const unsigned kCallNtOpenFile = 0x2; + const unsigned kCallNtQueryAttributesFile = 0x4; + const unsigned kCallNtQueryFullAttributesFile = 0x8; + const unsigned kCallNtSetInfoRename = 0x10; + + DWORD rule_to_add = kCallNtOpenFile | kCallNtCreateFile | + kCallNtQueryAttributesFile | + kCallNtQueryFullAttributesFile | kCallNtSetInfoRename; + + PolicyRule create(result); + PolicyRule open(result); + PolicyRule query(result); + PolicyRule query_full(result); + PolicyRule rename(result); + + switch (semantics) { + case TargetPolicy::FILES_ALLOW_DIR_ANY: { + open.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND); + create.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND); + break; + } + case TargetPolicy::FILES_ALLOW_READONLY: { + // We consider all flags that are not known to be readonly as potentially + // used for write. + DWORD allowed_flags = FILE_READ_DATA | FILE_READ_ATTRIBUTES | + FILE_READ_EA | SYNCHRONIZE | FILE_EXECUTE | + GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL; + DWORD restricted_flags = ~allowed_flags; + open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); + create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); + + // Read only access don't work for rename. + rule_to_add &= ~kCallNtSetInfoRename; + break; + } + case TargetPolicy::FILES_ALLOW_QUERY: { + // Here we don't want to add policy for the open or the create. + rule_to_add &= ~(kCallNtOpenFile | kCallNtCreateFile | + kCallNtSetInfoRename); + break; + } + case TargetPolicy::FILES_ALLOW_ANY: { + break; + } + default: { + NOTREACHED(); + return false; + } + } + + if ((rule_to_add & kCallNtCreateFile) && + (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) || + !policy->AddRule(IPC_NTCREATEFILE_TAG, &create))) { + return false; + } + + if ((rule_to_add & kCallNtOpenFile) && + (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) || + !policy->AddRule(IPC_NTOPENFILE_TAG, &open))) { + return false; + } + + if ((rule_to_add & kCallNtQueryAttributesFile) && + (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) || + !policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &query))) { + return false; + } + + if ((rule_to_add & kCallNtQueryFullAttributesFile) && + (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) + || !policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, + &query_full))) { + return false; + } + + if ((rule_to_add & kCallNtSetInfoRename) && + (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) || + !policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &rename))) { + return false; + } + + return true; +} + +// Right now we insert two rules, to be evaluated before any user supplied rule: +// - go to the broker if the path doesn't look like the paths that we push on +// the policy (namely \??\something). +// - go to the broker if it looks like this is a short-name path. +// +// It is possible to add a rule to go to the broker in any case; it would look +// something like: +// rule = new PolicyRule(ASK_BROKER); +// rule->AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); +// policy->AddRule(service, rule); +bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) { + PolicyRule format(ASK_BROKER); + PolicyRule short_name(ASK_BROKER); + + bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); + rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*", + CASE_SENSITIVE); + + rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); + rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE); + + if (!rv || !policy->AddRule(IPC_NTCREATEFILE_TAG, &format)) + return false; + + if (!policy->AddRule(IPC_NTCREATEFILE_TAG, &short_name)) + return false; + + if (!policy->AddRule(IPC_NTOPENFILE_TAG, &format)) + return false; + + if (!policy->AddRule(IPC_NTOPENFILE_TAG, &short_name)) + return false; + + if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &format)) + return false; + + if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &short_name)) + return false; + + if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &format)) + return false; + + if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &short_name)) + return false; + + if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &format)) + return false; + + if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &short_name)) + return false; + + return true; +} + +bool FileSystemPolicy::CreateFileAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &file, + uint32 attributes, + uint32 desired_access, + uint32 file_attributes, + uint32 share_access, + uint32 create_disposition, + uint32 create_options, + HANDLE *handle, + NTSTATUS* nt_status, + ULONG_PTR *io_information) { + // The only action supported is ASK_BROKER which means create the requested + // file as specified. + if (ASK_BROKER != eval_result) { + *nt_status = STATUS_ACCESS_DENIED; + return false; + } + IO_STATUS_BLOCK io_block = {0}; + UNICODE_STRING uni_name = {0}; + OBJECT_ATTRIBUTES obj_attributes = {0}; + InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); + *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes, + &io_block, file_attributes, share_access, + create_disposition, create_options, NULL, + 0, client_info.process); + + *io_information = io_block.Information; + return true; +} + +bool FileSystemPolicy::OpenFileAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &file, + uint32 attributes, + uint32 desired_access, + uint32 share_access, + uint32 open_options, + HANDLE *handle, + NTSTATUS* nt_status, + ULONG_PTR *io_information) { + // The only action supported is ASK_BROKER which means open the requested + // file as specified. + if (ASK_BROKER != eval_result) { + *nt_status = STATUS_ACCESS_DENIED; + return true; + } + // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and + // CreateDisposition = FILE_OPEN. + IO_STATUS_BLOCK io_block = {0}; + UNICODE_STRING uni_name = {0}; + OBJECT_ATTRIBUTES obj_attributes = {0}; + InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); + *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes, + &io_block, 0, share_access, FILE_OPEN, + open_options, NULL, 0, + client_info.process); + + *io_information = io_block.Information; + return true; +} + +bool FileSystemPolicy::QueryAttributesFileAction( + EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &file, + uint32 attributes, + FILE_BASIC_INFORMATION* file_info, + NTSTATUS* nt_status) { + // The only action supported is ASK_BROKER which means query the requested + // file as specified. + if (ASK_BROKER != eval_result) { + *nt_status = STATUS_ACCESS_DENIED; + return true; + } + + NtQueryAttributesFileFunction NtQueryAttributesFile = NULL; + ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile); + + UNICODE_STRING uni_name = {0}; + OBJECT_ATTRIBUTES obj_attributes = {0}; + InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); + *nt_status = NtQueryAttributesFile(&obj_attributes, file_info); + + return true; +} + +bool FileSystemPolicy::QueryFullAttributesFileAction( + EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &file, + uint32 attributes, + FILE_NETWORK_OPEN_INFORMATION* file_info, + NTSTATUS* nt_status) { + // The only action supported is ASK_BROKER which means query the requested + // file as specified. + if (ASK_BROKER != eval_result) { + *nt_status = STATUS_ACCESS_DENIED; + return true; + } + + NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = NULL; + ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile); + + UNICODE_STRING uni_name = {0}; + OBJECT_ATTRIBUTES obj_attributes = {0}; + InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); + *nt_status = NtQueryFullAttributesFile(&obj_attributes, file_info); + + return true; +} + +bool FileSystemPolicy::SetInformationFileAction( + EvalResult eval_result, const ClientInfo& client_info, + HANDLE target_file_handle, void* file_info, uint32 length, + uint32 info_class, IO_STATUS_BLOCK* io_block, + NTSTATUS* nt_status) { + // The only action supported is ASK_BROKER which means open the requested + // file as specified. + if (ASK_BROKER != eval_result) { + *nt_status = STATUS_ACCESS_DENIED; + return true; + } + + NtSetInformationFileFunction NtSetInformationFile = NULL; + ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile); + + HANDLE local_handle = NULL; + if (!::DuplicateHandle(client_info.process, target_file_handle, + ::GetCurrentProcess(), &local_handle, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + *nt_status = STATUS_ACCESS_DENIED; + return true; + } + + base::win::ScopedHandle handle(local_handle); + + FILE_INFORMATION_CLASS file_info_class = + static_cast(info_class); + *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length, + file_info_class); + + return true; +} + +bool PreProcessName(const std::wstring& path, std::wstring* new_path) { + ConvertToLongPath(path, new_path); + + bool reparsed = false; + if (ERROR_SUCCESS != IsReparsePoint(*new_path, &reparsed)) + return false; + + // We can't process reparsed file. + return !reparsed; +} + +} // namespace sandbox diff --git a/sandbox/win/src/filesystem_policy.h b/sandbox/win/src/filesystem_policy.h new file mode 100644 index 0000000..5010a9f --- /dev/null +++ b/sandbox/win/src/filesystem_policy.h @@ -0,0 +1,107 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_FILESYSTEM_POLICY_H__ +#define SANDBOX_SRC_FILESYSTEM_POLICY_H__ + +#include + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/policy_low_level.h" +#include "sandbox/src/sandbox_policy.h" + +namespace sandbox { + +enum EvalResult; + +// This class centralizes most of the knowledge related to file system policy +class FileSystemPolicy { + public: + // Creates the required low-level policy rules to evaluate a high-level + // policy rule for File IO, in particular open or create actions. + // 'name' is the file or directory name. + // 'semantics' is the desired semantics for the open or create. + // 'policy' is the policy generator to which the rules are going to be added. + static bool GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy); + + // Add basic file system rules. + static bool SetInitialRules(LowLevelPolicy* policy); + + // Performs the desired policy action on a create request with an + // API that is compatible with the IPC-received parameters. + // 'client_info' : the target process that is making the request. + // 'eval_result' : The desired policy action to accomplish. + // 'file' : The target file or directory. + static bool CreateFileAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &file, + uint32 attributes, + uint32 desired_access, + uint32 file_attributes, + uint32 share_access, + uint32 create_disposition, + uint32 create_options, + HANDLE* handle, + NTSTATUS* nt_status, + ULONG_PTR* io_information); + + // Performs the desired policy action on an open request with an + // API that is compatible with the IPC-received parameters. + // 'client_info' : the target process that is making the request. + // 'eval_result' : The desired policy action to accomplish. + // 'file' : The target file or directory. + static bool OpenFileAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &file, + uint32 attributes, + uint32 desired_access, + uint32 share_access, + uint32 open_options, + HANDLE* handle, + NTSTATUS* nt_status, + ULONG_PTR* io_information); + + // Performs the desired policy action on a query request with an + // API that is compatible with the IPC-received parameters. + static bool QueryAttributesFileAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &file, + uint32 attributes, + FILE_BASIC_INFORMATION* file_info, + NTSTATUS* nt_status); + + // Performs the desired policy action on a query request with an + // API that is compatible with the IPC-received parameters. + static bool QueryFullAttributesFileAction( + EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &file, + uint32 attributes, + FILE_NETWORK_OPEN_INFORMATION* file_info, + NTSTATUS* nt_status); + + // Performs the desired policy action on a set_info request with an + // API that is compatible with the IPC-received parameters. + static bool SetInformationFileAction(EvalResult eval_result, + const ClientInfo& client_info, + HANDLE target_file_handle, + void* file_info, + uint32 length, + uint32 info_class, + IO_STATUS_BLOCK* io_block, + NTSTATUS* nt_status); +}; + +// Expands the path and check if it's a reparse point. Returns false if +// we cannot determine or if there is an unexpected error. In that case +// the path cannot be trusted. +bool PreProcessName(const std::wstring& path, std::wstring* new_path); + +} // namespace sandbox + +#endif // SANDBOX_SRC_FILESYSTEM_POLICY_H__ diff --git a/sandbox/win/src/handle_closer.cc b/sandbox/win/src/handle_closer.cc new file mode 100644 index 0000000..d79f2c1 --- /dev/null +++ b/sandbox/win/src/handle_closer.cc @@ -0,0 +1,201 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/handle_closer.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/win/windows_version.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/internal_types.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/process_thread_interception.h" +#include "sandbox/src/win_utils.h" + +namespace { + +template T RoundUpToWordSize(T v) { + if (size_t mod = v % sizeof(size_t)) + v += sizeof(size_t) - mod; + return v; +} + +template T* RoundUpToWordSize(T* v) { + return reinterpret_cast(RoundUpToWordSize(reinterpret_cast(v))); +} + +} // namespace + +namespace sandbox { + +// Memory buffer mapped from the parent, with the list of handles. +SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close; + +HandleCloser::HandleCloser() {} + +ResultCode HandleCloser::AddHandle(const char16* handle_type, + const char16* handle_name) { + if (!handle_type) + return SBOX_ERROR_BAD_PARAMS; + + HandleMap::iterator names = handles_to_close_.find(handle_type); + if (names == handles_to_close_.end()) { // We have no entries for this type. + std::pair result = handles_to_close_.insert( + HandleMap::value_type(handle_type, HandleMap::mapped_type())); + names = result.first; + if (handle_name) + names->second.insert(handle_name); + } else if (!handle_name) { // Now we need to close all handles of this type. + names->second.clear(); + } else if (!names->second.empty()) { // Add another name for this type. + names->second.insert(handle_name); + } // If we're already closing all handles of type then we're done. + + return SBOX_ALL_OK; +} + +size_t HandleCloser::GetBufferSize() { + size_t bytes_total = offsetof(HandleCloserInfo, handle_entries); + + for (HandleMap::iterator i = handles_to_close_.begin(); + i != handles_to_close_.end(); ++i) { + size_t bytes_entry = offsetof(HandleListEntry, handle_type) + + (i->first.size() + 1) * sizeof(char16); + for (HandleMap::mapped_type::iterator j = i->second.begin(); + j != i->second.end(); ++j) { + bytes_entry += ((*j).size() + 1) * sizeof(char16); + } + + // Round up to the nearest multiple of word size. + bytes_entry = RoundUpToWordSize(bytes_entry); + bytes_total += bytes_entry; + } + + return bytes_total; +} + +bool HandleCloser::InitializeTargetHandles(TargetProcess* target) { + // Do nothing on an empty list (global pointer already initialized to NULL). + if (handles_to_close_.empty()) + return true; + + size_t bytes_needed = GetBufferSize(); + scoped_array local_buffer( + new size_t[bytes_needed / sizeof(size_t)]); + + if (!SetupHandleList(local_buffer.get(), bytes_needed)) + return false; + + HANDLE child = target->Process(); + + // Allocate memory in the target process without specifying the address + void* remote_data = ::VirtualAllocEx(child, NULL, bytes_needed, + MEM_COMMIT, PAGE_READWRITE); + if (NULL == remote_data) + return false; + + // Copy the handle buffer over. + SIZE_T bytes_written; + BOOL result = ::WriteProcessMemory(child, remote_data, local_buffer.get(), + bytes_needed, &bytes_written); + if (!result || bytes_written != bytes_needed) { + ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE); + return false; + } + + g_handles_to_close = reinterpret_cast(remote_data); + + ResultCode rc = target->TransferVariable("g_handles_to_close", + &g_handles_to_close, + sizeof(g_handles_to_close)); + + return (SBOX_ALL_OK == rc); +} + +bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) { + ::ZeroMemory(buffer, buffer_bytes); + HandleCloserInfo* handle_info = reinterpret_cast(buffer); + handle_info->record_bytes = buffer_bytes; + handle_info->num_handle_types = handles_to_close_.size(); + + char16* output = reinterpret_cast(&handle_info->handle_entries[0]); + char16* end = reinterpret_cast( + reinterpret_cast(buffer) + buffer_bytes); + for (HandleMap::iterator i = handles_to_close_.begin(); + i != handles_to_close_.end(); ++i) { + if (output >= end) + return false; + HandleListEntry* list_entry = reinterpret_cast(output); + output = &list_entry->handle_type[0]; + + // Copy the typename and set the offset and count. + i->first._Copy_s(output, i->first.size(), i->first.size()); + *(output += i->first.size()) = L'\0'; + output++; + list_entry->offset_to_names = reinterpret_cast(output) - + reinterpret_cast(list_entry); + list_entry->name_count = i->second.size(); + + // Copy the handle names. + for (HandleMap::mapped_type::iterator j = i->second.begin(); + j != i->second.end(); ++j) { + output = std::copy((*j).begin(), (*j).end(), output) + 1; + } + + // Round up to the nearest multiple of sizeof(size_t). + output = RoundUpToWordSize(output); + list_entry->record_bytes = reinterpret_cast(output) - + reinterpret_cast(list_entry); + } + + DCHECK_EQ(reinterpret_cast(output), reinterpret_cast(end)); + return output <= end; +} + +bool HandleCloser::SetupHandleInterceptions(InterceptionManager* manager) { + // We need to intercept CreateThread if we're closing ALPC port clients. + HandleMap::iterator names = handles_to_close_.find(L"ALPC Port"); + if (base::win::GetVersion() >= base::win::VERSION_VISTA && + names != handles_to_close_.end() && + (names->second.empty() || names->second.size() == 0)) { + if (!INTERCEPT_EAT(manager, kKerneldllName, CreateThread, + CREATE_THREAD_ID, 28)) { + return false; + } + if (!INTERCEPT_EAT(manager, kKerneldllName, GetUserDefaultLCID, + GET_USER_DEFAULT_LCID_ID, 4)) { + return false; + } + + return true; + } + + return true; +} + +bool GetHandleName(HANDLE handle, string16* handle_name) { + static NtQueryObject QueryObject = NULL; + if (!QueryObject) + ResolveNTFunctionPtr("NtQueryObject", &QueryObject); + + ULONG size = MAX_PATH; + scoped_ptr name; + NTSTATUS result; + + do { + name.reset(reinterpret_cast(new BYTE[size])); + result = QueryObject(handle, ObjectNameInformation, name.get(), + size, &size); + } while (result == STATUS_INFO_LENGTH_MISMATCH || + result == STATUS_BUFFER_OVERFLOW); + + if (NT_SUCCESS(result) && name->Buffer && name->Length) + handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t)); + else + handle_name->clear(); + + return NT_SUCCESS(result); +} + +} // namespace sandbox diff --git a/sandbox/win/src/handle_closer.h b/sandbox/win/src/handle_closer.h new file mode 100644 index 0000000..f680169 --- /dev/null +++ b/sandbox/win/src/handle_closer.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_HANDLE_CLOSER_H_ +#define SANDBOX_SRC_HANDLE_CLOSER_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/string16.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/target_process.h" + +namespace sandbox { + +// This is a map of handle-types to names that we need to close in the +// target process. A null set means we need to close all handles of the +// given type. +typedef std::map > HandleMap; + +// Type and set of corresponding handle names to close. +struct HandleListEntry { + size_t record_bytes; // Rounded to sizeof(size_t) bytes. + size_t offset_to_names; // Nul terminated strings of name_count names. + size_t name_count; + char16 handle_type[1]; +}; + +// Global parameters and a pointer to the list of entries. +struct HandleCloserInfo { + size_t record_bytes; // Rounded to sizeof(size_t) bytes. + size_t num_handle_types; + struct HandleListEntry handle_entries[1]; +}; + +SANDBOX_INTERCEPT HandleCloserInfo* g_handle_closer_info; + +// Adds handles to close after lockdown. +class HandleCloser { + public: + HandleCloser(); + + // Adds a handle that will be closed in the target process after lockdown. + // A NULL value for handle_name indicates all handles of the specified type. + // An empty string for handle_name indicates the handle is unnamed. + ResultCode AddHandle(const char16* handle_type, const char16* handle_name); + + // Serializes and copies the closer table into the target process. + bool InitializeTargetHandles(TargetProcess* target); + + // Adds any interceptions that may be required due to closed system handles. + bool SetupHandleInterceptions(InterceptionManager* manager); + + private: + // Calculates the memory needed to copy the serialized handles list (rounded + // to the nearest machine-word size). + size_t GetBufferSize(); + + // Serializes the handle list into the target process. + bool SetupHandleList(void* buffer, size_t buffer_bytes); + + HandleMap handles_to_close_; + + DISALLOW_COPY_AND_ASSIGN(HandleCloser); +}; + +// Returns the object manager's name associated with a handle +bool GetHandleName(HANDLE handle, string16* handle_name); + +} // namespace sandbox + +#endif // SANDBOX_SRC_HANDLE_CLOSER_H_ diff --git a/sandbox/win/src/handle_closer_agent.cc b/sandbox/win/src/handle_closer_agent.cc new file mode 100644 index 0000000..2b5ac97 --- /dev/null +++ b/sandbox/win/src/handle_closer_agent.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/handle_closer_agent.h" + +#include "base/logging.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/win_utils.h" + +namespace { + +// Returns type infomation for an NT object. This routine is expected to be +// called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions +// that can be generated when handle tracing is enabled. +NTSTATUS QueryObjectTypeInformation(HANDLE handle, + void* buffer, + ULONG* size) { + static NtQueryObject QueryObject = NULL; + if (!QueryObject) + ResolveNTFunctionPtr("NtQueryObject", &QueryObject); + + NTSTATUS status = STATUS_UNSUCCESSFUL; + __try { + status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size); + } __except(GetExceptionCode() == STATUS_INVALID_HANDLE ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + status = STATUS_INVALID_HANDLE; + } + return status; +} + +} // namespace + +namespace sandbox { + +// Memory buffer mapped from the parent, with the list of handles. +SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close = NULL; + +bool HandleCloserAgent::NeedsHandlesClosed() { + return g_handles_to_close != NULL; +} + +// Reads g_handles_to_close and creates the lookup map. +void HandleCloserAgent::InitializeHandlesToClose() { + CHECK(g_handles_to_close != NULL); + + // Grab the header. + HandleListEntry* entry = g_handles_to_close->handle_entries; + for (size_t i = 0; i < g_handles_to_close->num_handle_types; ++i) { + // Set the type name. + char16* input = entry->handle_type; + HandleMap::mapped_type& handle_names = handles_to_close_[input]; + input = reinterpret_cast(reinterpret_cast(entry) + + entry->offset_to_names); + // Grab all the handle names. + for (size_t j = 0; j < entry->name_count; ++j) { + std::pair name + = handle_names.insert(input); + CHECK(name.second); + input += name.first->size() + 1; + } + + // Move on to the next entry. + entry = reinterpret_cast(reinterpret_cast(entry) + + entry->record_bytes); + + DCHECK(reinterpret_cast(entry) >= input); + DCHECK(reinterpret_cast(entry) - input < + sizeof(size_t) / sizeof(char16)); + } + + // Clean up the memory we copied over. + ::VirtualFree(g_handles_to_close, 0, MEM_RELEASE); + g_handles_to_close = NULL; +} + +bool HandleCloserAgent::CloseHandles() { + DWORD handle_count = UINT_MAX; + const int kInvalidHandleThreshold = 100; + const size_t kHandleOffset = sizeof(HANDLE); + + if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count)) + return false; + + // Set up buffers for the type info and the name. + std::vector type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) + + 32 * sizeof(wchar_t)); + OBJECT_TYPE_INFORMATION* type_info = + reinterpret_cast(&(type_info_buffer[0])); + string16 handle_name; + HANDLE handle = NULL; + int invalid_count = 0; + + // Keep incrementing until we hit the number of handles reported by + // GetProcessHandleCount(). If we hit a very long sequence of invalid + // handles we assume that we've run past the end of the table. + while (handle_count && invalid_count < kInvalidHandleThreshold) { + reinterpret_cast(handle) += kHandleOffset; + NTSTATUS rc; + + // Get the type name, reusing the buffer. + ULONG size = static_cast(type_info_buffer.size()); + rc = QueryObjectTypeInformation(handle, type_info, &size); + while (rc == STATUS_INFO_LENGTH_MISMATCH || + rc == STATUS_BUFFER_OVERFLOW) { + type_info_buffer.resize(size + sizeof(wchar_t)); + type_info = reinterpret_cast( + &(type_info_buffer[0])); + rc = QueryObjectTypeInformation(handle, type_info, &size); + // Leave padding for the nul terminator. + if (NT_SUCCESS(0) && size == type_info_buffer.size()) + rc = STATUS_INFO_LENGTH_MISMATCH; + } + if (!NT_SUCCESS(rc) || !type_info->Name.Buffer) { + ++invalid_count; + continue; + } + + --handle_count; + type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0'; + + // Check if we're looking for this type of handle. + HandleMap::iterator result = + handles_to_close_.find(type_info->Name.Buffer); + if (result != handles_to_close_.end()) { + HandleMap::mapped_type& names = result->second; + // Empty set means close all handles of this type; otherwise check name. + if (!names.empty()) { + // Move on to the next handle if this name doesn't match. + if (!GetHandleName(handle, &handle_name) || !names.count(handle_name)) + continue; + } + + if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0)) + return false; + if (!::CloseHandle(handle)) + return false; + } + } + + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/handle_closer_agent.h b/sandbox/win/src/handle_closer_agent.h new file mode 100644 index 0000000..c74987c --- /dev/null +++ b/sandbox/win/src/handle_closer_agent.h @@ -0,0 +1,37 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_ +#define SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_ + +#include "base/basictypes.h" +#include "base/string16.h" +#include "sandbox/src/handle_closer.h" +#include "sandbox/src/sandbox_types.h" + +namespace sandbox { + +// Target process code to close the handle list copied over from the broker. +class HandleCloserAgent { + public: + HandleCloserAgent() {} + + // Reads the serialized list from the broker and creates the lookup map. + void InitializeHandlesToClose(); + + // Closes any handles matching those in the lookup map. + bool CloseHandles(); + + // True if we have handles waiting to be closed + static bool NeedsHandlesClosed(); + + private: + HandleMap handles_to_close_; + + DISALLOW_COPY_AND_ASSIGN(HandleCloserAgent); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_ diff --git a/sandbox/win/src/handle_closer_test.cc b/sandbox/win/src/handle_closer_test.cc new file mode 100644 index 0000000..81b6db2 --- /dev/null +++ b/sandbox/win/src/handle_closer_test.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stringprintf.h" +#include "base/win/scoped_handle.h" +#include "sandbox/src/handle_closer_agent.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/target_services.h" +#include "sandbox/tests/common/controller.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const wchar_t *kFileExtensions[] = { L".1", L".2", L".3", L".4" }; + +// Returns a handle to a unique marker file that can be retrieved between runs. +HANDLE GetMarkerFile(const wchar_t *extension) { + wchar_t path_buffer[MAX_PATH + 1]; + CHECK(::GetTempPath(MAX_PATH, path_buffer)); + string16 marker_path = path_buffer; + marker_path += L"\\sbox_marker_"; + + // Generate a unique value from the exe's size and timestamp. + CHECK(::GetModuleFileName(NULL, path_buffer, MAX_PATH)); + base::win::ScopedHandle module(::CreateFile(path_buffer, + FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); + CHECK(module.IsValid()); + FILETIME timestamp; + CHECK(::GetFileTime(module, ×tamp, NULL, NULL)); + marker_path += base::StringPrintf(L"%08x%08x%08x", + ::GetFileSize(module, NULL), + timestamp.dwLowDateTime, + timestamp.dwHighDateTime); + marker_path += extension; + + // Make the file delete-on-close so cleanup is automatic. + return CreateFile(marker_path.c_str(), FILE_ALL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL); +} + +// Used by the thread pool tests. +HANDLE finish_event; +const int kWaitCount = 20; + +} // namespace + +namespace sandbox { + +// Checks for the presence of a list of files (in object path form). +// Format: CheckForFileHandle (Y|N) \path\to\file1 [\path\to\file2 ...] +// - Y or N depending if the file should exist or not. +SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t **argv) { + if (argc < 2) + return SBOX_TEST_FAILED_TO_RUN_TEST; + bool should_find = argv[0][0] == L'Y'; + if (argv[0][1] != L'\0' || !should_find && argv[0][0] != L'N') + return SBOX_TEST_FAILED_TO_RUN_TEST; + + static int state = BEFORE_INIT; + switch (state++) { + case BEFORE_INIT: + // Create a unique marker file that is open while the test is running. + // The handles leak, but it will be closed by the test or on exit. + for (int i = 0; i < arraysize(kFileExtensions); ++i) + EXPECT_NE(GetMarkerFile(kFileExtensions[i]), INVALID_HANDLE_VALUE); + return SBOX_TEST_SUCCEEDED; + + case AFTER_REVERT: { + // Brute force the handle table to find what we're looking for. + DWORD handle_count = UINT_MAX; + const int kInvalidHandleThreshold = 100; + const size_t kHandleOffset = sizeof(HANDLE); + HANDLE handle = NULL; + int invalid_count = 0; + string16 handle_name; + + if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count)) + return SBOX_TEST_FAILED_TO_RUN_TEST; + + while (handle_count && invalid_count < kInvalidHandleThreshold) { + reinterpret_cast(handle) += kHandleOffset; + if (GetHandleName(handle, &handle_name)) { + for (int i = 1; i < argc; ++i) { + if (handle_name == argv[i]) + return should_find ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; + } + --handle_count; + } else { + ++invalid_count; + } + } + + return should_find ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED; + } + + default: // Do nothing. + break; + } + + return SBOX_TEST_SUCCEEDED; +} + +TEST(HandleCloserTest, CheckForMarkerFiles) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(EVERY_STATE); + sandbox::TargetPolicy* policy = runner.GetPolicy(); + + string16 command = string16(L"CheckForFileHandles Y"); + for (int i = 0; i < arraysize(kFileExtensions); ++i) { + string16 handle_name; + base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i])); + CHECK(marker.IsValid()); + CHECK(sandbox::GetHandleName(marker, &handle_name)); + command += (L" "); + command += handle_name; + } + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) << + "Failed: " << command; +} + +TEST(HandleCloserTest, CloseMarkerFiles) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(EVERY_STATE); + sandbox::TargetPolicy* policy = runner.GetPolicy(); + + string16 command = string16(L"CheckForFileHandles N"); + for (int i = 0; i < arraysize(kFileExtensions); ++i) { + string16 handle_name; + base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i])); + CHECK(marker.IsValid()); + CHECK(sandbox::GetHandleName(marker, &handle_name)); + CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()), + SBOX_ALL_OK); + command += (L" "); + command += handle_name; + } + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) << + "Failed: " << command; +} + +void WINAPI ThreadPoolTask(void* event, BOOLEAN timeout) { + static volatile LONG waiters_remaining = kWaitCount; + CHECK(!timeout); + CHECK(::CloseHandle(event)); + if (::InterlockedDecrement(&waiters_remaining) == 0) + CHECK(::SetEvent(finish_event)); +} + +// Run a thread pool inside a sandbox without a CSRSS connection. +SBOX_TESTS_COMMAND int RunThreadPool(int argc, wchar_t **argv) { + HANDLE wait_list[20]; + CHECK(finish_event = ::CreateEvent(NULL, TRUE, FALSE, NULL)); + + // Set up a bunch of waiters. + HANDLE pool = NULL; + for (int i = 0; i < kWaitCount; ++i) { + HANDLE event = ::CreateEvent(NULL, TRUE, FALSE, NULL); + CHECK(event); + CHECK(::RegisterWaitForSingleObject(&pool, event, ThreadPoolTask, event, + INFINITE, WT_EXECUTEONLYONCE)); + wait_list[i] = event; + } + + // Signal all the waiters. + for (int i = 0; i < kWaitCount; ++i) + CHECK(::SetEvent(wait_list[i])); + + CHECK_EQ(::WaitForSingleObject(finish_event, INFINITE), WAIT_OBJECT_0); + CHECK(::CloseHandle(finish_event)); + + return SBOX_TEST_SUCCEEDED; +} + +TEST(HandleCloserTest, RunThreadPool) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(AFTER_REVERT); + sandbox::TargetPolicy* policy = runner.GetPolicy(); + + // Sever the CSRSS connection by closing ALPC ports inside the sandbox. + CHECK_EQ(policy->AddKernelObjectToClose(L"ALPC Port", NULL), SBOX_ALL_OK); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"RunThreadPool")); +} + +} // namespace sandbox diff --git a/sandbox/win/src/handle_dispatcher.cc b/sandbox/win/src/handle_dispatcher.cc new file mode 100644 index 0000000..7a18cee --- /dev/null +++ b/sandbox/win/src/handle_dispatcher.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/handle_dispatcher.h" + +#include "base/win/scoped_handle.h" +#include "sandbox/src/handle_interception.h" +#include "sandbox/src/handle_policy.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_broker.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/sandbox_utils.h" + +namespace sandbox { + +HandleDispatcher::HandleDispatcher(PolicyBase* policy_base) + : policy_base_(policy_base) { + static const IPCCall duplicate_handle_proxy = { + {IPC_DUPLICATEHANDLEPROXY_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE, + ULONG_TYPE}, + reinterpret_cast(&HandleDispatcher::DuplicateHandleProxy) + }; + + ipc_calls_.push_back(duplicate_handle_proxy); +} + +bool HandleDispatcher::SetupService(InterceptionManager* manager, + int service) { + // We perform no interceptions for handles right now. + switch (service) { + case IPC_DUPLICATEHANDLEPROXY_TAG: + return true; + } + + return false; +} + +bool HandleDispatcher::DuplicateHandleProxy(IPCInfo* ipc, + HANDLE source_handle, + DWORD target_process_id, + DWORD desired_access, + DWORD options) { + NTSTATUS error; + static NtQueryObject QueryObject = NULL; + if (!QueryObject) + ResolveNTFunctionPtr("NtQueryObject", &QueryObject); + + // Get a copy of the handle for use in the broker process. + HANDLE handle_temp; + if (!::DuplicateHandle(ipc->client_info->process, source_handle, + ::GetCurrentProcess(), &handle_temp, + 0, FALSE, DUPLICATE_SAME_ACCESS)) { + ipc->return_info.win32_result = ::GetLastError(); + return false; + } + base::win::ScopedHandle handle(handle_temp); + + // Get the object type (32 characters is safe; current max is 14). + BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)]; + OBJECT_TYPE_INFORMATION* type_info = + reinterpret_cast(buffer); + ULONG size = sizeof(buffer) - sizeof(wchar_t); + error = QueryObject(handle, ObjectTypeInformation, type_info, size, &size); + if (!NT_SUCCESS(error)) { + ipc->return_info.win32_result = error; + return false; + } + type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0'; + + CountedParameterSet params; + params[HandleTarget::NAME] = ParamPickerMake(type_info->Name.Buffer); + params[HandleTarget::TARGET] = ParamPickerMake(target_process_id); + + EvalResult eval = policy_base_->EvalPolicy(IPC_DUPLICATEHANDLEPROXY_TAG, + params.GetBase()); + ipc->return_info.win32_result = + HandlePolicy::DuplicateHandleProxyAction(eval, *ipc->client_info, + source_handle, + target_process_id, + &ipc->return_info.handle, + desired_access, options); + return true; +} + +} // namespace sandbox + diff --git a/sandbox/win/src/handle_dispatcher.h b/sandbox/win/src/handle_dispatcher.h new file mode 100644 index 0000000..c1abc28 --- /dev/null +++ b/sandbox/win/src/handle_dispatcher.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_HANDLE_DISPATCHER_H_ +#define SANDBOX_SRC_HANDLE_DISPATCHER_H_ + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sandbox_policy_base.h" + +namespace sandbox { + +// This class handles handle-related IPC calls. +class HandleDispatcher : public Dispatcher { + public: + explicit HandleDispatcher(PolicyBase* policy_base); + ~HandleDispatcher() {} + + // Dispatcher interface. + virtual bool SetupService(InterceptionManager* manager, int service); + + private: + // Processes IPC requests coming from calls to + // TargetServices::DuplicateHandle() in the target. + bool DuplicateHandleProxy(IPCInfo* ipc, HANDLE source_handle, + DWORD target_process_id, DWORD desired_access, + DWORD options); + + PolicyBase* policy_base_; + DISALLOW_COPY_AND_ASSIGN(HandleDispatcher); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_HANDLE_DISPATCHER_H_ + diff --git a/sandbox/win/src/handle_interception.cc b/sandbox/win/src/handle_interception.cc new file mode 100644 index 0000000..0f7b9f8 --- /dev/null +++ b/sandbox/win/src/handle_interception.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/handle_interception.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +ResultCode DuplicateHandleProxy(HANDLE source_handle, + DWORD target_process_id, + HANDLE* target_handle, + DWORD desired_access, + DWORD options) { + *target_handle = NULL; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + return SBOX_ERROR_NO_SPACE; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_DUPLICATEHANDLEPROXY_TAG, + source_handle, target_process_id, + desired_access, options, &answer); + if (SBOX_ALL_OK != code) + return code; + + if (answer.win32_result) { + ::SetLastError(answer.nt_status); + return SBOX_ERROR_GENERIC; + } + + *target_handle = answer.handle; + return SBOX_ALL_OK; +} + +} // namespace sandbox + diff --git a/sandbox/win/src/handle_interception.h b/sandbox/win/src/handle_interception.h new file mode 100644 index 0000000..543c7ba --- /dev/null +++ b/sandbox/win/src/handle_interception.h @@ -0,0 +1,24 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +#ifndef SANDBOX_SRC_HANDLE_INTERCEPTION_H_ +#define SANDBOX_SRC_HANDLE_INTERCEPTION_H_ + +namespace sandbox { + +// TODO(jschuh) Add an interception to catch dangerous DuplicateHandle calls. + +ResultCode DuplicateHandleProxy(HANDLE source_handle, + DWORD target_process_id, + HANDLE* target_handle, + DWORD desired_access, + DWORD options); + +} // namespace sandbox + +#endif // SANDBOX_SRC_HANDLE_INTERCEPTION_H_ + diff --git a/sandbox/win/src/handle_policy.cc b/sandbox/win/src/handle_policy.cc new file mode 100644 index 0000000..355dda8 --- /dev/null +++ b/sandbox/win/src/handle_policy.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/handle_policy.h" + +#include + +#include "base/win/scoped_handle.h" +#include "sandbox/src/broker_services.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_engine_opcodes.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/sandbox_utils.h" + +namespace sandbox { + +bool HandlePolicy::GenerateRules(const wchar_t* type_name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy) { + PolicyRule duplicate_rule(ASK_BROKER); + + switch (semantics) { + case TargetPolicy::HANDLES_DUP_ANY: { + if (!duplicate_rule.AddNumberMatch(IF_NOT, HandleTarget::TARGET, + ::GetCurrentProcessId(), EQUAL)) { + return false; + } + break; + } + + case TargetPolicy::HANDLES_DUP_BROKER: { + if (!duplicate_rule.AddNumberMatch(IF, HandleTarget::TARGET, + ::GetCurrentProcessId(), EQUAL)) { + return false; + } + break; + } + + default: + return false; + } + if (!duplicate_rule.AddStringMatch(IF, HandleTarget::NAME, type_name, + CASE_INSENSITIVE)) { + return false; + } + if (!policy->AddRule(IPC_DUPLICATEHANDLEPROXY_TAG, &duplicate_rule)) { + return false; + } + return true; +} + +DWORD HandlePolicy::DuplicateHandleProxyAction(EvalResult eval_result, + const ClientInfo& client_info, + HANDLE source_handle, + DWORD target_process_id, + HANDLE* target_handle, + DWORD desired_access, + DWORD options) { + // The only action supported is ASK_BROKER which means duplicate the handle. + if (ASK_BROKER != eval_result) { + return ERROR_ACCESS_DENIED; + } + + base::win::ScopedHandle remote_target_process; + if (target_process_id != ::GetCurrentProcessId()) { + // Sandboxed children are dynamic, so we check that manually. + if (!BrokerServicesBase::GetInstance()->IsActiveTarget(target_process_id)) { + return ERROR_ACCESS_DENIED; + } + + remote_target_process.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, + target_process_id)); + if (!remote_target_process.IsValid()) + return ::GetLastError(); + } + + // If the policy didn't block us and we have no valid target, then the broker + // (this process) is the valid target. + HANDLE target_process = remote_target_process.IsValid() ? + remote_target_process : ::GetCurrentProcess(); + DWORD result = ERROR_SUCCESS; + if (!::DuplicateHandle(client_info.process, source_handle, target_process, + target_handle, desired_access, FALSE, + options)) { + return ::GetLastError(); + } + + return ERROR_SUCCESS; +} + +} // namespace sandbox + diff --git a/sandbox/win/src/handle_policy.h b/sandbox/win/src/handle_policy.h new file mode 100644 index 0000000..c3b7156 --- /dev/null +++ b/sandbox/win/src/handle_policy.h @@ -0,0 +1,41 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_HANDLE_POLICY_H_ +#define SANDBOX_SRC_HANDLE_POLICY_H_ + +#include + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/policy_low_level.h" +#include "sandbox/src/sandbox_policy.h" + +namespace sandbox { + +enum EvalResult; + +// This class centralizes most of the knowledge related to handle policy. +class HandlePolicy { + public: + // Creates the required low-level policy rules to evaluate a high-level + // policy rule for handles, in particular duplicate action. + static bool GenerateRules(const wchar_t* type_name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy); + + // Processes a 'TargetPolicy::DuplicateHandle()' request from the target. + static DWORD DuplicateHandleProxyAction(EvalResult eval_result, + const ClientInfo& client_info, + HANDLE source_handle, + DWORD target_process_id, + HANDLE* target_handle, + DWORD desired_access, + DWORD options); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_HANDLE_POLICY_H_ + diff --git a/sandbox/win/src/handle_policy_test.cc b/sandbox/win/src/handle_policy_test.cc new file mode 100644 index 0000000..05eb39b --- /dev/null +++ b/sandbox/win/src/handle_policy_test.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stringprintf.h" +#include "sandbox/src/handle_policy.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/win_utils.h" +#include "sandbox/tests/common/controller.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +// Just waits for the supplied number of milliseconds. +SBOX_TESTS_COMMAND int Handle_WaitProcess(int argc, wchar_t **argv) { + if (argc != 1) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + ::Sleep(::wcstoul(argv[0], NULL, 10)); + return SBOX_TEST_TIMED_OUT; +} + +// Attempts to duplicate an event handle into the target process. +SBOX_TESTS_COMMAND int Handle_DuplicateEvent(int argc, wchar_t **argv) { + if (argc != 1) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + // Create a test event to use as a handle. + base::win::ScopedHandle test_event; + test_event.Set(::CreateEvent(NULL, TRUE, TRUE, NULL)); + if (!test_event.IsValid()) + return SBOX_TEST_FIRST_ERROR; + + // Get the target process ID. + DWORD target_process_id = ::wcstoul(argv[0], NULL, 10); + + HANDLE handle = NULL; + ResultCode result = SandboxFactory::GetTargetServices()->DuplicateHandle( + test_event, target_process_id, &handle, 0, DUPLICATE_SAME_ACCESS); + + return (result == SBOX_ALL_OK) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED; +} + +// Tests that duplicating an object works only when the policy allows it. +TEST(HandlePolicyTest, DuplicateHandle) { + TestRunner target; + TestRunner runner; + + // Kick off an asynchronous target process for testing. + target.SetAsynchronous(true); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000")); + + // First test that we fail to open the event. + std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", + target.process_id()); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); + + // Now successfully open the event after adding a duplicate handle rule. + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES, + TargetPolicy::HANDLES_DUP_ANY, + L"Event")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str())); +} + +// Tests that duplicating an object works only when the policy allows it. +TEST(HandlePolicyTest, DuplicatePeerHandle) { + TestRunner target; + TestRunner runner; + + // Kick off an asynchronous target process for testing. + target.SetAsynchronous(true); + target.SetUnsandboxed(true); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000")); + + // First test that we fail to open the event. + std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", + target.process_id()); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); + + // Now successfully open the event after adding a duplicate handle rule. + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES, + TargetPolicy::HANDLES_DUP_ANY, + L"Event")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str())); +} + +// Tests that duplicating an object works only when the policy allows it. +TEST(HandlePolicyTest, DuplicateBrokerHandle) { + TestRunner runner; + + // First test that we fail to open the event. + std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d", + ::GetCurrentProcessId()); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); + + // Add the peer rule and make sure we fail again. + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES, + TargetPolicy::HANDLES_DUP_ANY, + L"Event")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str())); + + + // Now successfully open the event after adding a broker handle rule. + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES, + TargetPolicy::HANDLES_DUP_BROKER, + L"Event")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str())); +} + +} // namespace sandbox + diff --git a/sandbox/win/src/handle_table.cc b/sandbox/win/src/handle_table.cc new file mode 100644 index 0000000..c7fcf0a --- /dev/null +++ b/sandbox/win/src/handle_table.cc @@ -0,0 +1,181 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/handle_table.h" + +#include +#include + +#include "base/memory/scoped_ptr.h" +#include "sandbox/src/win_utils.h" + +namespace { + +bool CompareHandleEntries(const SYSTEM_HANDLE_INFORMATION& a, + const SYSTEM_HANDLE_INFORMATION& b) { + return a.ProcessId < b.ProcessId; +} + +} // namespace + +namespace sandbox { + +const char16* HandleTable::kTypeProcess = L"Process"; +const char16* HandleTable::kTypeThread = L"Thread"; +const char16* HandleTable::kTypeFile = L"File"; +const char16* HandleTable::kTypeDirectory = L"Directory"; +const char16* HandleTable::kTypeKey = L"Key"; +const char16* HandleTable::kTypeWindowStation = L"WindowStation"; +const char16* HandleTable::kTypeDesktop = L"Desktop"; +const char16* HandleTable::kTypeService = L"Service"; +const char16* HandleTable::kTypeMutex = L"Mutex"; +const char16* HandleTable::kTypeSemaphore = L"Semaphore"; +const char16* HandleTable::kTypeEvent = L"Event"; +const char16* HandleTable::kTypeTimer = L"Timer"; +const char16* HandleTable::kTypeNamedPipe = L"NamedPipe"; +const char16* HandleTable::kTypeJobObject = L"JobObject"; +const char16* HandleTable::kTypeFileMap = L"FileMap"; +const char16* HandleTable::kTypeAlpcPort = L"ALPC Port"; + +HandleTable::HandleTable() { + static NtQuerySystemInformation QuerySystemInformation = NULL; + if (!QuerySystemInformation) + ResolveNTFunctionPtr("NtQuerySystemInformation", &QuerySystemInformation); + + ULONG size = 0x15000; + NTSTATUS result; + do { + handle_info_buffer_.resize(size); + result = QuerySystemInformation(SystemHandleInformation, + handle_info_internal(), size, &size); + } while (result == STATUS_INFO_LENGTH_MISMATCH); + + // We failed, so make an empty table. + if (!NT_SUCCESS(result)) { + handle_info_buffer_.resize(0); + return; + } + + // Sort it to make process lookups faster. + std::sort(handle_info_internal()->Information, + handle_info_internal()->Information + + handle_info_internal()->NumberOfHandles, CompareHandleEntries); +} + +HandleTable::Iterator HandleTable::HandlesForProcess(ULONG process_id) const { + SYSTEM_HANDLE_INFORMATION key; + key.ProcessId = process_id; + + const SYSTEM_HANDLE_INFORMATION* start = handle_info()->Information; + const SYSTEM_HANDLE_INFORMATION* finish = + &handle_info()->Information[handle_info()->NumberOfHandles]; + + start = std::lower_bound(start, finish, key, CompareHandleEntries); + if (start->ProcessId != process_id) + return Iterator(*this, finish, finish); + finish = std::upper_bound(start, finish, key, CompareHandleEntries); + return Iterator(*this, start, finish); +} + +HandleTable::HandleEntry::HandleEntry( + const SYSTEM_HANDLE_INFORMATION* handle_info_entry) + : handle_entry_(handle_info_entry), last_entry_(0) { +} + +void HandleTable::HandleEntry::UpdateInfo(UpdateType flag) { + static NtQueryObject QueryObject = NULL; + if (!QueryObject) + ResolveNTFunctionPtr("NtQueryObject", &QueryObject); + + NTSTATUS result; + + // Always update the basic type info, but grab the names as needed. + if (needs_info_update()) { + handle_name_.clear(); + type_name_.clear(); + last_entry_ = handle_entry_; + + // Most handle names are very short, so start small and reuse this buffer. + if (type_info_buffer_.empty()) + type_info_buffer_.resize(sizeof(OBJECT_TYPE_INFORMATION) + + (32 * sizeof(wchar_t))); + ULONG size = static_cast(type_info_buffer_.size()); + result = QueryObject(reinterpret_cast(handle_entry_->Handle), + ObjectTypeInformation, type_info_internal(), size, &size); + while (result == STATUS_INFO_LENGTH_MISMATCH) { + type_info_buffer_.resize(size); + result = QueryObject(reinterpret_cast(handle_entry_->Handle), + ObjectTypeInformation, type_info_internal(), size, &size); + } + + if (!NT_SUCCESS(result)) { + type_info_buffer_.clear(); + return; + } + } + + // Don't bother copying out names until we ask for them, and then cache them. + switch (flag) { + case UPDATE_INFO_AND_NAME: + if (type_info_buffer_.size() && handle_name_.empty()) { + ULONG size = MAX_PATH; + scoped_ptr name; + do { + name.reset(reinterpret_cast(new BYTE[size])); + result = QueryObject(reinterpret_cast( + handle_entry_->Handle), ObjectNameInformation, name.get(), + size, &size); + } while (result == STATUS_INFO_LENGTH_MISMATCH); + + if (NT_SUCCESS(result)) { + handle_name_.assign(name->Buffer, name->Length / sizeof(wchar_t)); + } + } + break; + + case UPDATE_INFO_AND_TYPE_NAME: + if (!type_info_buffer_.empty() && type_info_internal()->Name.Buffer && + type_name_.empty()) { + type_name_.assign(type_info_internal()->Name.Buffer, + type_info_internal()->Name.Length / sizeof(wchar_t)); + } + break; + } +} + +const OBJECT_TYPE_INFORMATION* HandleTable::HandleEntry::TypeInfo() { + UpdateInfo(UPDATE_INFO_ONLY); + return type_info_buffer_.empty() ? NULL : type_info_internal(); +} + +const string16& HandleTable::HandleEntry::Name() { + UpdateInfo(UPDATE_INFO_AND_NAME); + return handle_name_; +} + +const string16& HandleTable::HandleEntry::Type() { + UpdateInfo(UPDATE_INFO_AND_TYPE_NAME); + return type_name_; +} + +bool HandleTable::HandleEntry::IsType(const string16& type_string) { + UpdateInfo(UPDATE_INFO_ONLY); + if (type_info_buffer_.empty()) + return false; + return type_string.compare(0, + type_info_internal()->Name.Length / sizeof(wchar_t), + type_info_internal()->Name.Buffer) == 0; +} + +HandleTable::Iterator::Iterator(const HandleTable& table, + const SYSTEM_HANDLE_INFORMATION* start, + const SYSTEM_HANDLE_INFORMATION* end) + : table_(table), current_(start), end_(end) { +} + +HandleTable::Iterator::Iterator(const Iterator& it) + : table_(it.table_), current_(it.current_.handle_entry_), end_(it.end_) { +} + +} // namespace sandbox diff --git a/sandbox/win/src/handle_table.h b/sandbox/win/src/handle_table.h new file mode 100644 index 0000000..e2b2615 --- /dev/null +++ b/sandbox/win/src/handle_table.h @@ -0,0 +1,159 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_HANDLE_TABLE_H_ +#define SANDBOX_SRC_HANDLE_TABLE_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/string16.h" +#include "sandbox/src/nt_internals.h" + +namespace sandbox { + +// HandleTable retrieves the global handle table and provides helper classes +// for iterating through the table and retrieving handle info. +class HandleTable { + public: + static const char16* HandleTable::kTypeProcess; + static const char16* HandleTable::kTypeThread; + static const char16* HandleTable::kTypeFile; + static const char16* HandleTable::kTypeDirectory; + static const char16* HandleTable::kTypeKey; + static const char16* HandleTable::kTypeWindowStation; + static const char16* HandleTable::kTypeDesktop; + static const char16* HandleTable::kTypeService; + static const char16* HandleTable::kTypeMutex; + static const char16* HandleTable::kTypeSemaphore; + static const char16* HandleTable::kTypeEvent; + static const char16* HandleTable::kTypeTimer; + static const char16* HandleTable::kTypeNamedPipe; + static const char16* HandleTable::kTypeJobObject; + static const char16* HandleTable::kTypeFileMap; + static const char16* HandleTable::kTypeAlpcPort; + + class Iterator; + + // Used by the iterator to provide simple caching accessors to handle data. + class HandleEntry { + public: + bool operator==(const HandleEntry& rhs) const { + return handle_entry_ == rhs.handle_entry_; + } + + bool operator!=(const HandleEntry& rhs) const { + return handle_entry_ != rhs.handle_entry_; + } + + const SYSTEM_HANDLE_INFORMATION* handle_entry() const { + return handle_entry_; + } + + const OBJECT_TYPE_INFORMATION* TypeInfo(); + + const string16& Name(); + + const string16& Type(); + + bool IsType(const string16& type_string); + + private: + friend class Iterator; + friend class HandleTable; + + enum UpdateType { + UPDATE_INFO_ONLY, + UPDATE_INFO_AND_NAME, + UPDATE_INFO_AND_TYPE_NAME, + }; + + explicit HandleEntry(const SYSTEM_HANDLE_INFORMATION* handle_info_entry); + + bool needs_info_update() { return handle_entry_ != last_entry_; } + + void UpdateInfo(UpdateType flag); + + OBJECT_TYPE_INFORMATION* type_info_internal() { + return reinterpret_cast( + &(type_info_buffer_[0])); + } + + const SYSTEM_HANDLE_INFORMATION* handle_entry_; + const SYSTEM_HANDLE_INFORMATION* last_entry_; + std::vector type_info_buffer_; + string16 handle_name_; + string16 type_name_; + + DISALLOW_COPY_AND_ASSIGN(HandleEntry); + }; + + class Iterator { + public: + Iterator(const HandleTable& table, const SYSTEM_HANDLE_INFORMATION* start, + const SYSTEM_HANDLE_INFORMATION* stop); + + Iterator(const Iterator& it); + + Iterator& operator++() { + if (++(current_.handle_entry_) == end_) + current_.handle_entry_ = table_.end(); + return *this; + } + + bool operator==(const Iterator& rhs) const { + return current_ == rhs.current_; + } + + bool operator!=(const Iterator& rhs) const { + return current_ != rhs.current_; + } + + HandleEntry& operator*() { return current_; } + + operator const SYSTEM_HANDLE_INFORMATION*() { + return current_.handle_entry_; + } + + HandleEntry* operator->() { return ¤t_; } + + private: + const HandleTable& table_; + HandleEntry current_; + const SYSTEM_HANDLE_INFORMATION* end_; + }; + + HandleTable(); + + Iterator begin() const { + return Iterator(*this, handle_info()->Information, + &handle_info()->Information[handle_info()->NumberOfHandles]); + } + + const SYSTEM_HANDLE_INFORMATION_EX* handle_info() const { + return reinterpret_cast( + &(handle_info_buffer_[0])); + } + + // Returns an iterator to the handles for only the supplied process ID. + Iterator HandlesForProcess(ULONG process_id) const; + const SYSTEM_HANDLE_INFORMATION* end() const { + return &handle_info()->Information[handle_info()->NumberOfHandles]; + } + + private: + SYSTEM_HANDLE_INFORMATION_EX* handle_info_internal() { + return reinterpret_cast( + &(handle_info_buffer_[0])); + } + + std::vector handle_info_buffer_; + + DISALLOW_COPY_AND_ASSIGN(HandleTable); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_HANDLE_TABLE_H_ diff --git a/sandbox/win/src/integrity_level_test.cc b/sandbox/win/src/integrity_level_test.cc new file mode 100644 index 0000000..c6e0b64 --- /dev/null +++ b/sandbox/win/src/integrity_level_test.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "base/win/windows_version.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/tests/common/controller.h" + +namespace sandbox { + + +SBOX_TESTS_COMMAND int CheckIntegrityLevel(int argc, wchar_t **argv) { + ATL::CAccessToken token; + if (!token.GetEffectiveToken(TOKEN_READ)) + return SBOX_TEST_FAILED; + + char* buffer[100]; + DWORD buf_size = 100; + if (!::GetTokenInformation(token.GetHandle(), TokenIntegrityLevel, + reinterpret_cast(buffer), buf_size, + &buf_size)) + return SBOX_TEST_FAILED; + + TOKEN_MANDATORY_LABEL* label = + reinterpret_cast(buffer); + + PSID sid_low = NULL; + if (!::ConvertStringSidToSid(L"S-1-16-4096", &sid_low)) + return SBOX_TEST_FAILED; + + BOOL is_low_sid = ::EqualSid(label->Label.Sid, sid_low); + + ::LocalFree(sid_low); + + if (is_low_sid) + return SBOX_TEST_SUCCEEDED; + + return SBOX_TEST_DENIED; +} + +TEST(IntegrityLevelTest, TestLowILReal) { + if (base::win::GetVersion() != base::win::VERSION_VISTA) + return; + + TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE); + + runner.SetTimeout(INFINITE); + + runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel")); + + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel")); +} + +TEST(DelayedIntegrityLevelTest, TestLowILDelayed) { + if (base::win::GetVersion() != base::win::VERSION_VISTA) + return; + + TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE); + + runner.SetTimeout(INFINITE); + + runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel")); + + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel")); +} + +TEST(IntegrityLevelTest, TestNoILChange) { + if (base::win::GetVersion() != base::win::VERSION_VISTA) + return; + + TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE); + + runner.SetTimeout(INFINITE); + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel")); +} + +} // namespace sandbox diff --git a/sandbox/win/src/interception.cc b/sandbox/win/src/interception.cc new file mode 100644 index 0000000..a850c6e --- /dev/null +++ b/sandbox/win/src/interception.cc @@ -0,0 +1,551 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// For information about interceptions as a whole see +// http://dev.chromium.org/developers/design-documents/sandbox . + +#include + +#include "sandbox/src/interception.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/win/pe_image.h" +#include "base/win/windows_version.h" +#include "sandbox/src/interception_internal.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_utils.h" +#include "sandbox/src/service_resolver.h" +#include "sandbox/src/target_interceptions.h" +#include "sandbox/src/target_process.h" +#include "sandbox/src/wow64.h" + +namespace { + +const char kMapViewOfSectionName[] = "NtMapViewOfSection"; +const char kUnmapViewOfSectionName[] = "NtUnmapViewOfSection"; + +// Standard allocation granularity and page size for Windows. +const size_t kAllocGranularity = 65536; +const size_t kPageSize = 4096; + +// Find a random offset within 64k and aligned to ceil(log2(size)). +size_t GetGranularAlignedRandomOffset(size_t size) { + CHECK_LE(size, kAllocGranularity); + unsigned int offset; + + do { + rand_s(&offset); + offset &= (kAllocGranularity - 1); + } while (offset > (kAllocGranularity - size)); + + // Find an alignment between 64 and the page size (4096). + size_t align_size = kPageSize; + for (size_t new_size = align_size / 2; new_size >= size; new_size /= 2) { + align_size = new_size; + } + return offset & ~(align_size - 1); +} + +} // namespace + +namespace sandbox { + +SANDBOX_INTERCEPT SharedMemory* g_interceptions; + +// Table of the unpatched functions that we intercept. Mapped from the parent. +SANDBOX_INTERCEPT OriginalFunctions g_originals = { NULL }; + +// Magic constant that identifies that this function is not to be patched. +const char kUnloadDLLDummyFunction[] = "@"; + +InterceptionManager::InterceptionManager(TargetProcess* child_process, + bool relaxed) + : child_(child_process), names_used_(false), relaxed_(relaxed) { + child_->AddRef(); +} +InterceptionManager::~InterceptionManager() { + child_->Release(); +} + +bool InterceptionManager::AddToPatchedFunctions( + const wchar_t* dll_name, const char* function_name, + InterceptionType interception_type, const void* replacement_code_address, + InterceptorId id) { + InterceptionData function; + function.type = interception_type; + function.id = id; + function.dll = dll_name; + function.function = function_name; + function.interceptor_address = replacement_code_address; + + interceptions_.push_back(function); + return true; +} + +bool InterceptionManager::AddToPatchedFunctions( + const wchar_t* dll_name, const char* function_name, + InterceptionType interception_type, const char* replacement_function_name, + InterceptorId id) { + InterceptionData function; + function.type = interception_type; + function.id = id; + function.dll = dll_name; + function.function = function_name; + function.interceptor = replacement_function_name; + function.interceptor_address = NULL; + + interceptions_.push_back(function); + names_used_ = true; + return true; +} + +bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) { + InterceptionData module_to_unload; + module_to_unload.type = INTERCEPTION_UNLOAD_MODULE; + module_to_unload.dll = dll_name; + // The next two are dummy values that make the structures regular, instead + // of having special cases. They should not be used. + module_to_unload.function = kUnloadDLLDummyFunction; + module_to_unload.interceptor_address = reinterpret_cast(1); + + interceptions_.push_back(module_to_unload); + return true; +} + +bool InterceptionManager::InitializeInterceptions() { + if (interceptions_.empty()) + return true; // Nothing to do here + + size_t buffer_bytes = GetBufferSize(); + scoped_array local_buffer(new char[buffer_bytes]); + + if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes)) + return false; + + void* remote_buffer; + if (!CopyDataToChild(local_buffer.get(), buffer_bytes, &remote_buffer)) + return false; + + bool hot_patch_needed = (0 != buffer_bytes); + if (!PatchNtdll(hot_patch_needed)) + return false; + + g_interceptions = reinterpret_cast(remote_buffer); + ResultCode rc = child_->TransferVariable("g_interceptions", + &g_interceptions, + sizeof(g_interceptions)); + return (SBOX_ALL_OK == rc); +} + +size_t InterceptionManager::GetBufferSize() const { + std::set dlls; + size_t buffer_bytes = 0; + + std::list::const_iterator it = interceptions_.begin(); + for (; it != interceptions_.end(); ++it) { + // skip interceptions that are performed from the parent + if (!IsInterceptionPerformedByChild(*it)) + continue; + + if (!dlls.count(it->dll)) { + // NULL terminate the dll name on the structure + size_t dll_name_bytes = (it->dll.size() + 1) * sizeof(wchar_t); + + // include the dll related size + buffer_bytes += RoundUpToMultiple(offsetof(DllPatchInfo, dll_name) + + dll_name_bytes, sizeof(size_t)); + dlls.insert(it->dll); + } + + // we have to NULL terminate the strings on the structure + size_t strings_chars = it->function.size() + it->interceptor.size() + 2; + + // a new FunctionInfo is required per function + size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars; + record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t)); + buffer_bytes += record_bytes; + } + + if (0 != buffer_bytes) + // add the part of SharedMemory that we have not counted yet + buffer_bytes += offsetof(SharedMemory, dll_list); + + return buffer_bytes; +} + +// Basically, walk the list of interceptions moving them to the config buffer, +// but keeping together all interceptions that belong to the same dll. +// The config buffer is a local buffer, not the one allocated on the child. +bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) { + if (0 == buffer_bytes) + return true; + + DCHECK(buffer_bytes > sizeof(SharedMemory)); + + SharedMemory* shared_memory = reinterpret_cast(buffer); + DllPatchInfo* dll_info = shared_memory->dll_list; + int num_dlls = 0; + + shared_memory->interceptor_base = names_used_ ? child_->MainModule() : NULL; + + buffer_bytes -= offsetof(SharedMemory, dll_list); + buffer = dll_info; + + std::list::iterator it = interceptions_.begin(); + for (; it != interceptions_.end();) { + // skip interceptions that are performed from the parent + if (!IsInterceptionPerformedByChild(*it)) { + ++it; + continue; + } + + const std::wstring dll = it->dll; + if (!SetupDllInfo(*it, &buffer, &buffer_bytes)) + return false; + + // walk the interceptions from this point, saving the ones that are + // performed on this dll, and removing the entry from the list. + // advance the iterator before removing the element from the list + std::list::iterator rest = it; + for (; rest != interceptions_.end();) { + if (rest->dll == dll) { + if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info)) + return false; + if (it == rest) + ++it; + rest = interceptions_.erase(rest); + } else { + ++rest; + } + } + dll_info = reinterpret_cast(buffer); + ++num_dlls; + } + + shared_memory->num_intercepted_dlls = num_dlls; + return true; +} + +// Fills up just the part that depends on the dll, not the info that depends on +// the actual interception. +bool InterceptionManager::SetupDllInfo(const InterceptionData& data, + void** buffer, + size_t* buffer_bytes) const { + DCHECK(buffer_bytes); + DCHECK(buffer); + DCHECK(*buffer); + + DllPatchInfo* dll_info = reinterpret_cast(*buffer); + + // the strings have to be zero terminated + size_t required = offsetof(DllPatchInfo, dll_name) + + (data.dll.size() + 1) * sizeof(wchar_t); + required = RoundUpToMultiple(required, sizeof(size_t)); + if (*buffer_bytes < required) + return false; + + *buffer_bytes -= required; + *buffer = reinterpret_cast(*buffer) + required; + + // set up the dll info to be what we know about it at this time + dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE); + dll_info->record_bytes = required; + dll_info->offset_to_functions = required; + dll_info->num_functions = 0; + data.dll._Copy_s(dll_info->dll_name, data.dll.size(), data.dll.size()); + dll_info->dll_name[data.dll.size()] = L'\0'; + + return true; +} + +bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data, + void** buffer, + size_t* buffer_bytes, + DllPatchInfo* dll_info) const { + DCHECK(buffer_bytes); + DCHECK(buffer); + DCHECK(*buffer); + + if ((dll_info->unload_module) && + (data.function != kUnloadDLLDummyFunction)) { + // Can't specify a dll for both patch and unload. + NOTREACHED(); + } + + FunctionInfo* function = reinterpret_cast(*buffer); + + size_t name_bytes = data.function.size(); + size_t interceptor_bytes = data.interceptor.size(); + + // the strings at the end of the structure are zero terminated + size_t required = offsetof(FunctionInfo, function) + + name_bytes + interceptor_bytes + 2; + required = RoundUpToMultiple(required, sizeof(size_t)); + if (*buffer_bytes < required) + return false; + + // update the caller's values + *buffer_bytes -= required; + *buffer = reinterpret_cast(*buffer) + required; + + function->record_bytes = required; + function->type = data.type; + function->id = data.id; + function->interceptor_address = data.interceptor_address; + char* names = function->function; + + data.function._Copy_s(names, name_bytes, name_bytes); + names += name_bytes; + *names++ = '\0'; + + // interceptor follows the function_name + data.interceptor._Copy_s(names, interceptor_bytes, interceptor_bytes); + names += interceptor_bytes; + *names++ = '\0'; + + // update the dll table + dll_info->num_functions++; + dll_info->record_bytes += required; + + return true; +} + +bool InterceptionManager::CopyDataToChild(const void* local_buffer, + size_t buffer_bytes, + void** remote_buffer) const { + DCHECK(NULL != remote_buffer); + if (0 == buffer_bytes) { + *remote_buffer = NULL; + return true; + } + + HANDLE child = child_->Process(); + + // Allocate memory on the target process without specifying the address + void* remote_data = ::VirtualAllocEx(child, NULL, buffer_bytes, + MEM_COMMIT, PAGE_READWRITE); + if (NULL == remote_data) + return false; + + SIZE_T bytes_written; + BOOL success = ::WriteProcessMemory(child, remote_data, local_buffer, + buffer_bytes, &bytes_written); + if (FALSE == success || bytes_written != buffer_bytes) { + ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE); + return false; + } + + *remote_buffer = remote_data; + + return true; +} + +// Only return true if the child should be able to perform this interception. +bool InterceptionManager::IsInterceptionPerformedByChild( + const InterceptionData& data) const { + if (INTERCEPTION_INVALID == data.type) + return false; + + if (INTERCEPTION_SERVICE_CALL == data.type) + return false; + + if (data.type >= INTERCEPTION_LAST) + return false; + + std::wstring ntdll(kNtdllName); + if (ntdll == data.dll) + return false; // ntdll has to be intercepted from the parent + + return true; +} + +bool InterceptionManager::PatchNtdll(bool hot_patch_needed) { + // Maybe there is nothing to do + if (!hot_patch_needed && interceptions_.empty()) + return true; + + if (hot_patch_needed) { +#if SANDBOX_EXPORTS + // Make sure the functions are not excluded by the linker. +#if defined(_WIN64) + #pragma comment(linker, "/include:TargetNtMapViewOfSection64") + #pragma comment(linker, "/include:TargetNtUnmapViewOfSection64") +#else + #pragma comment(linker, "/include:_TargetNtMapViewOfSection@44") + #pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12") +#endif +#endif + ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44); + ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12); + } + + // Reserve a full 64k memory range in the child process. + HANDLE child = child_->Process(); + BYTE* thunk_base = reinterpret_cast( + ::VirtualAllocEx(child, NULL, kAllocGranularity, + MEM_RESERVE, PAGE_NOACCESS)); + + // Find an aligned, random location within the reserved range. + size_t thunk_bytes = interceptions_.size() * sizeof(ThunkData) + + sizeof(DllInterceptionData); + size_t thunk_offset = GetGranularAlignedRandomOffset(thunk_bytes); + + // Split the base and offset along page boundaries. + thunk_base += thunk_offset & ~(kPageSize - 1); + thunk_offset &= kPageSize - 1; + + // Make an aligned, padded allocation, and move the pointer to our chunk. + size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & kPageSize; + thunk_base = reinterpret_cast( + ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded, + MEM_COMMIT, PAGE_EXECUTE_READWRITE)); + CHECK(thunk_base); // If this fails we'd crash anyway on an invalid access. + DllInterceptionData* thunks = reinterpret_cast( + thunk_base + thunk_offset); + + DllInterceptionData dll_data; + dll_data.data_bytes = thunk_bytes; + dll_data.num_thunks = 0; + dll_data.used_bytes = offsetof(DllInterceptionData, thunks); + + // Reset all helpers for a new child. + memset(g_originals, 0, sizeof(g_originals)); + + // this should write all the individual thunks to the child's memory + if (!PatchClientFunctions(thunks, thunk_bytes, &dll_data)) + return false; + + // and now write the first part of the table to the child's memory + SIZE_T written; + bool ok = FALSE != ::WriteProcessMemory(child, thunks, &dll_data, + offsetof(DllInterceptionData, thunks), + &written); + + if (!ok || (offsetof(DllInterceptionData, thunks) != written)) + return false; + + // Attempt to protect all the thunks, but ignore failure + DWORD old_protection; + ::VirtualProtectEx(child, thunks, thunk_bytes, + PAGE_EXECUTE_READ, &old_protection); + + ResultCode ret = child_->TransferVariable("g_originals", g_originals, + sizeof(g_originals)); + + return SBOX_ALL_OK == ret ? true : false; +} + +bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks, + size_t thunk_bytes, + DllInterceptionData* dll_data) { + DCHECK(NULL != thunks); + DCHECK(NULL != dll_data); + + HMODULE ntdll_base = ::GetModuleHandle(kNtdllName); + if (!ntdll_base) + return false; + + base::win::PEImage ntdll_image(ntdll_base); + + // Bypass purify's interception. + wchar_t* loader_get = reinterpret_cast( + ntdll_image.GetProcAddress("LdrGetDllHandle")); + if (loader_get) { + if (!GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + loader_get, &ntdll_base)) + return false; + } + + if (base::win::GetVersion() <= base::win::VERSION_VISTA) { + Wow64 WowHelper(child_, ntdll_base); + if (!WowHelper.WaitForNtdll()) + return false; + } + + char* interceptor_base = NULL; + +#if SANDBOX_EXPORTS + interceptor_base = reinterpret_cast(child_->MainModule()); + HMODULE local_interceptor = ::LoadLibrary(child_->Name()); +#endif + + ServiceResolverThunk* thunk; +#if defined(_WIN64) + thunk = new ServiceResolverThunk(child_->Process(), relaxed_); +#else + base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); + if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { + if (os_info->version() >= base::win::VERSION_WIN8) + thunk = new Wow64W8ResolverThunk(child_->Process(), relaxed_); + else + thunk = new Wow64ResolverThunk(child_->Process(), relaxed_); + } else if (!IsXPSP2OrLater()) { + thunk = new Win2kResolverThunk(child_->Process(), relaxed_); + } else if (os_info->version() >= base::win::VERSION_WIN8) { + thunk = new Win8ResolverThunk(child_->Process(), relaxed_); + } else { + thunk = new ServiceResolverThunk(child_->Process(), relaxed_); + } +#endif + + std::list::iterator it = interceptions_.begin(); + for (; it != interceptions_.end(); ++it) { + const std::wstring ntdll(kNtdllName); + if (it->dll != ntdll) + break; + + if (INTERCEPTION_SERVICE_CALL != it->type) + break; + +#if SANDBOX_EXPORTS + // We may be trying to patch by function name. + if (NULL == it->interceptor_address) { + const char* address; + NTSTATUS ret = thunk->ResolveInterceptor(local_interceptor, + it->interceptor.c_str(), + reinterpret_cast( + &address)); + if (!NT_SUCCESS(ret)) + break; + + // Translate the local address to an address on the child. + it->interceptor_address = interceptor_base + (address - + reinterpret_cast(local_interceptor)); + } +#endif + NTSTATUS ret = thunk->Setup(ntdll_base, + interceptor_base, + it->function.c_str(), + it->interceptor.c_str(), + it->interceptor_address, + &thunks->thunks[dll_data->num_thunks], + thunk_bytes - dll_data->used_bytes, + NULL); + if (!NT_SUCCESS(ret)) + break; + + DCHECK(!g_originals[it->id]); + g_originals[it->id] = &thunks->thunks[dll_data->num_thunks]; + + dll_data->num_thunks++; + dll_data->used_bytes += sizeof(ThunkData); + } + + delete(thunk); + +#if SANDBOX_EXPORTS + if (NULL != local_interceptor) + ::FreeLibrary(local_interceptor); +#endif + + if (it != interceptions_.end()) + return false; + + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/interception.h b/sandbox/win/src/interception.h new file mode 100644 index 0000000..c02b0ff --- /dev/null +++ b/sandbox/win/src/interception.h @@ -0,0 +1,272 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines InterceptionManager, the class in charge of setting up interceptions +// for the sandboxed process. For more details see +// http://dev.chromium.org/developers/design-documents/sandbox . + +#ifndef SANDBOX_SRC_INTERCEPTION_H_ +#define SANDBOX_SRC_INTERCEPTION_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/gtest_prod_util.h" +#include "sandbox/src/sandbox_types.h" + +namespace sandbox { + +class TargetProcess; +enum InterceptorId; + +// Internal structures used for communication between the broker and the target. +struct DllPatchInfo; +struct DllInterceptionData; + +// The InterceptionManager executes on the parent application, and it is in +// charge of setting up the desired interceptions, and placing the Interception +// Agent into the child application. +// +// The exposed API consists of two methods: AddToPatchedFunctions to set up a +// particular interception, and InitializeInterceptions to actually go ahead and +// perform all interceptions and transfer data to the child application. +// +// The typical usage is something like this: +// +// InterceptionManager interception_manager(child); +// if (!interception_manager.AddToPatchedFunctions( +// L"ntdll.dll", "NtCreateFile", +// sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1)) +// return false; +// +// if (!interception_manager.AddToPatchedFunctions( +// L"kernel32.dll", "CreateDirectoryW", +// sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2)) +// return false; +// +// if (!interception_manager.InitializeInterceptions()) { +// DWORD error = ::GetLastError(); +// return false; +// } +// +// Any required syncronization must be performed outside this class. Also, it is +// not possible to perform further interceptions after InitializeInterceptions +// is called. +// +class InterceptionManager { + // The unit test will access private members. + // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes + // do not work with sandbox tests. + FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1); + FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2); + + public: + // An interception manager performs interceptions on a given child process. + // If we are allowed to intercept functions that have been patched by somebody + // else, relaxed should be set to true. + // Note: We increase the child's reference count internally. + InterceptionManager(TargetProcess* child_process, bool relaxed); + ~InterceptionManager(); + + // Patches function_name inside dll_name to point to replacement_code_address. + // function_name has to be an exported symbol of dll_name. + // Returns true on success. + // + // The new function should match the prototype and calling convention of the + // function to intercept except for one extra argument (the first one) that + // contains a pointer to the original function, to simplify the development + // of interceptors (for IA32). In x64, there is no extra argument to the + // interceptor, so the provided InterceptorId is used to keep a table of + // intercepted functions so that the interceptor can index that table to get + // the pointer that would have been the first argument (g_originals[id]). + // + // For example, to intercept NtClose, the following code could be used: + // + // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); + // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose, + // IN HANDLE Handle) { + // // do something + // // call the original function + // return OriginalClose(Handle); + // } + // + // And in x64: + // + // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); + // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) { + // // do something + // // call the original function + // NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID]; + // return OriginalClose(Handle); + // } + bool AddToPatchedFunctions(const wchar_t* dll_name, + const char* function_name, + InterceptionType interception_type, + const void* replacement_code_address, + InterceptorId id); + + // Patches function_name inside dll_name to point to + // replacement_function_name. + bool AddToPatchedFunctions(const wchar_t* dll_name, + const char* function_name, + InterceptionType interception_type, + const char* replacement_function_name, + InterceptorId id); + + // The interception agent will unload the dll with dll_name. + bool AddToUnloadModules(const wchar_t* dll_name); + + // Initializes all interceptions on the client. + // Returns true on success. + // + // The child process must be created suspended, and cannot be resumed until + // after this method returns. In addition, no action should be performed on + // the child that may cause it to resume momentarily, such as injecting + // threads or APCs. + // + // This function must be called only once, after all interceptions have been + // set up using AddToPatchedFunctions. + bool InitializeInterceptions(); + + private: + // Used to store the interception information until the actual set-up. + struct InterceptionData { + InterceptionType type; // Interception type. + InterceptorId id; // Interceptor id. + std::wstring dll; // Name of dll to intercept. + std::string function; // Name of function to intercept. + std::string interceptor; // Name of interceptor function. + const void* interceptor_address; // Interceptor's entry point. + }; + + // Calculates the size of the required configuration buffer. + size_t GetBufferSize() const; + + // Rounds up the size of a given buffer, considering alignment (padding). + // value is the current size of the buffer, and alignment is specified in + // bytes. + static inline size_t RoundUpToMultiple(size_t value, size_t alignment) { + return ((value + alignment -1) / alignment) * alignment; + } + + // Sets up a given buffer with all the information that has to be transfered + // to the child. + // Returns true on success. + // + // The buffer size should be at least the value returned by GetBufferSize + bool SetupConfigBuffer(void* buffer, size_t buffer_bytes); + + // Fills up the part of the transfer buffer that corresponds to information + // about one dll to patch. + // data is the first recorded interception for this dll. + // Returns true on success. + // + // On successful return, buffer will be advanced from it's current position + // to the point where the next block of configuration data should be written + // (the actual interception info), and the current size of the buffer will + // decrease to account the space used by this method. + bool SetupDllInfo(const InterceptionData& data, + void** buffer, size_t* buffer_bytes) const; + + // Fills up the part of the transfer buffer that corresponds to a single + // function to patch. + // dll_info points to the dll being updated with the interception stored on + // data. The buffer pointer and remaining size are updated by this call. + // Returns true on success. + bool SetupInterceptionInfo(const InterceptionData& data, void** buffer, + size_t* buffer_bytes, + DllPatchInfo* dll_info) const; + + // Returns true if this interception is to be performed by the child + // as opposed to from the parent. + bool IsInterceptionPerformedByChild(const InterceptionData& data) const; + + // Allocates a buffer on the child's address space (returned on + // remote_buffer), and fills it with the contents of a local buffer. + // Returns true on success. + bool CopyDataToChild(const void* local_buffer, size_t buffer_bytes, + void** remote_buffer) const; + + // Performs the cold patch (from the parent) of ntdll. + // Returns true on success. + // + // This method will insert additional interceptions to launch the interceptor + // agent on the child process, if there are additional interceptions to do. + bool PatchNtdll(bool hot_patch_needed); + + // Peforms the actual interceptions on ntdll. + // thunks is the memory to store all the thunks for this dll (on the child), + // and dll_data is a local buffer to hold global dll interception info. + // Returns true on success. + bool PatchClientFunctions(DllInterceptionData* thunks, + size_t thunk_bytes, + DllInterceptionData* dll_data); + + // The process to intercept. + TargetProcess* child_; + // Holds all interception info until the call to initialize (perform the + // actual patch). + std::list interceptions_; + + // Keep track of patches added by name. + bool names_used_; + + // true if we are allowed to patch already-patched functions. + bool relaxed_; + + DISALLOW_COPY_AND_ASSIGN(InterceptionManager); +}; + +// This macro simply calls interception_manager.AddToPatchedFunctions with +// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that +// the interceptor is called "TargetXXX", where XXX is the name of the service. +// Note that num_params is the number of bytes to pop out of the stack for +// the exported interceptor, following the calling convention of a service call +// (WINAPI = with the "C" underscore). +#if SANDBOX_EXPORTS +#if defined(_WIN64) +#define MAKE_SERVICE_NAME(service, params) "Target" # service "64" +#else +#define MAKE_SERVICE_NAME(service, params) "_Target" # service "@" # params +#endif + +#define ADD_NT_INTERCEPTION(service, id, num_params) \ + AddToPatchedFunctions(kNtdllName, #service, \ + sandbox::INTERCEPTION_SERVICE_CALL, \ + MAKE_SERVICE_NAME(service, num_params), id) + +#define INTERCEPT_NT(manager, service, id, num_params) \ + ((&Target##service) ? \ + manager->ADD_NT_INTERCEPTION(service, id, num_params) : false) + +#define INTERCEPT_EAT(manager, dll, function, id, num_params) \ + ((&Target##function) ? \ + manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ + MAKE_SERVICE_NAME(function, num_params), \ + id) : \ + false) +#else // SANDBOX_EXPORTS +#if defined(_WIN64) +#define MAKE_SERVICE_NAME(service) &Target##service##64 +#else +#define MAKE_SERVICE_NAME(service) &Target##service +#endif + +#define ADD_NT_INTERCEPTION(service, id, num_params) \ + AddToPatchedFunctions(kNtdllName, #service, \ + sandbox::INTERCEPTION_SERVICE_CALL, \ + MAKE_SERVICE_NAME(service), id) + +#define INTERCEPT_NT(manager, service, id, num_params) \ + manager->ADD_NT_INTERCEPTION(service, id, num_params) + +#define INTERCEPT_EAT(manager, dll, function, id, num_params) \ + manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ + MAKE_SERVICE_NAME(function), id) +#endif // SANDBOX_EXPORTS + +} // namespace sandbox + +#endif // SANDBOX_SRC_INTERCEPTION_H_ diff --git a/sandbox/win/src/interception_agent.cc b/sandbox/win/src/interception_agent.cc new file mode 100644 index 0000000..b40364f --- /dev/null +++ b/sandbox/win/src/interception_agent.cc @@ -0,0 +1,233 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// For information about interceptions as a whole see +// http://dev.chromium.org/developers/design-documents/sandbox . + +#include "sandbox/src/interception_agent.h" + +#include "sandbox/src/interception_internal.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/eat_resolver.h" +#include "sandbox/src/sidestep_resolver.h" +#include "sandbox/src/sandbox_nt_util.h" + +namespace { + +// Returns true if target lies between base and base + range. +bool IsWithinRange(const void* base, size_t range, const void* target) { + const char* end = reinterpret_cast(base) + range; + return reinterpret_cast(target) < end; +} + +} // namespace + +namespace sandbox { + +// This is the list of all imported symbols from ntdll.dll. +SANDBOX_INTERCEPT NtExports g_nt; + +// The list of intercepted functions back-pointers. +SANDBOX_INTERCEPT OriginalFunctions g_originals; + +// Memory buffer mapped from the parent, with the list of interceptions. +SANDBOX_INTERCEPT SharedMemory* g_interceptions = NULL; + +InterceptionAgent* InterceptionAgent::GetInterceptionAgent() { + static InterceptionAgent* s_singleton = NULL; + if (!s_singleton) { + if (!g_interceptions) + return NULL; + + size_t array_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*); + s_singleton = reinterpret_cast( + new(NT_ALLOC) char[array_bytes + sizeof(InterceptionAgent)]); + + bool success = s_singleton->Init(g_interceptions); + if (!success) { + operator delete(s_singleton, NT_ALLOC); + s_singleton = NULL; + } + } + return s_singleton; +} + +bool InterceptionAgent::Init(SharedMemory* shared_memory) { + interceptions_ = shared_memory; + for (int i = 0 ; i < shared_memory->num_intercepted_dlls; i++) + dlls_[i] = NULL; + return true; +} + +bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path, + const UNICODE_STRING* name, + const DllPatchInfo* dll_info) { + UNICODE_STRING current_name; + current_name.Length = static_cast(g_nt.wcslen(dll_info->dll_name) * + sizeof(wchar_t)); + current_name.MaximumLength = current_name.Length; + current_name.Buffer = const_cast(dll_info->dll_name); + + BOOLEAN case_insensitive = TRUE; + if (full_path && + !g_nt.RtlCompareUnicodeString(¤t_name, full_path, case_insensitive)) + return true; + + if (name && + !g_nt.RtlCompareUnicodeString(¤t_name, name, case_insensitive)) + return true; + + return false; +} + +bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, + const UNICODE_STRING* name, + void* base_address) { + DllPatchInfo* dll_info = interceptions_->dll_list; + int i = 0; + for (; i < interceptions_->num_intercepted_dlls; i++) { + if (DllMatch(full_path, name, dll_info)) + break; + + dll_info = reinterpret_cast( + reinterpret_cast(dll_info) + dll_info->record_bytes); + } + + // Return now if the dll is not in our list of interest. + if (i == interceptions_->num_intercepted_dlls) + return true; + + // The dll must be unloaded. + if (dll_info->unload_module) + return false; + + // Purify causes this condition to trigger. + if (dlls_[i]) + return true; + + size_t buffer_bytes = offsetof(DllInterceptionData, thunks) + + dll_info->num_functions * sizeof(ThunkData); + dlls_[i] = reinterpret_cast( + new(NT_PAGE, base_address) char[buffer_bytes]); + + DCHECK_NT(dlls_[i]); + if (!dlls_[i]) + return true; + + dlls_[i]->data_bytes = buffer_bytes; + dlls_[i]->num_thunks = 0; + dlls_[i]->base = base_address; + dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks); + + VERIFY(PatchDll(dll_info, dlls_[i])); + + ULONG old_protect; + SIZE_T real_size = buffer_bytes; + void* to_protect = dlls_[i]; + VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect, + &real_size, PAGE_EXECUTE_READ, + &old_protect)); + return true; +} + +void InterceptionAgent::OnDllUnload(void* base_address) { + for (int i = 0; i < interceptions_->num_intercepted_dlls; i++) { + if (dlls_[i] && dlls_[i]->base == base_address) { + operator delete(dlls_[i], NT_PAGE); + dlls_[i] = NULL; + break; + } + } +} + +// TODO(rvargas): We have to deal with prebinded dlls. I see two options: change +// the timestamp of the patched dll, or modify the info on the prebinded dll. +// the first approach messes matching of debug symbols, the second one is more +// complicated. +bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info, + DllInterceptionData* thunks) { + DCHECK_NT(NULL != thunks); + DCHECK_NT(NULL != dll_info); + + const FunctionInfo* function = reinterpret_cast( + reinterpret_cast(dll_info) + dll_info->offset_to_functions); + + for (int i = 0; i < dll_info->num_functions; i++) { + if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) { + NOTREACHED_NT(); + return false; + } + + ResolverThunk* resolver = GetResolver(function->type); + if (!resolver) + return false; + + const char* interceptor = function->function + + g_nt.strlen(function->function) + 1; + + if (!IsWithinRange(function, function->record_bytes, interceptor) || + !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) { + NOTREACHED_NT(); + return false; + } + + NTSTATUS ret = resolver->Setup(thunks->base, + interceptions_->interceptor_base, + function->function, + interceptor, + function->interceptor_address, + &thunks->thunks[i], + sizeof(ThunkData), + NULL); + if (!NT_SUCCESS(ret)) { + NOTREACHED_NT(); + return false; + } + + DCHECK_NT(!g_originals[function->id]); + g_originals[function->id] = &thunks->thunks[i]; + + thunks->num_thunks++; + thunks->used_bytes += sizeof(ThunkData); + + function = reinterpret_cast( + reinterpret_cast(function) + function->record_bytes); + } + + return true; +} + +// This method is called from within the loader lock +ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) { + static EatResolverThunk* eat_resolver = NULL; + static SidestepResolverThunk* sidestep_resolver = NULL; + static SmartSidestepResolverThunk* smart_sidestep_resolver = NULL; + + if (!eat_resolver) + eat_resolver = new(NT_ALLOC) EatResolverThunk; + +#if !defined(_WIN64) + // Sidestep is not supported for x64. + if (!sidestep_resolver) + sidestep_resolver = new(NT_ALLOC) SidestepResolverThunk; + + if (!smart_sidestep_resolver) + smart_sidestep_resolver = new(NT_ALLOC) SmartSidestepResolverThunk; +#endif + + switch (type) { + case INTERCEPTION_EAT: + return eat_resolver; + case INTERCEPTION_SIDESTEP: + return sidestep_resolver; + case INTERCEPTION_SMART_SIDESTEP: + return smart_sidestep_resolver; + default: + NOTREACHED_NT(); + } + + return NULL; +} + +} // namespace sandbox diff --git a/sandbox/win/src/interception_agent.h b/sandbox/win/src/interception_agent.h new file mode 100644 index 0000000..10679dd --- /dev/null +++ b/sandbox/win/src/interception_agent.h @@ -0,0 +1,87 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines InterceptionAgent, the class in charge of setting up interceptions +// from the inside of the sandboxed process. For more details see +// http://dev.chromium.org/developers/design-documents/sandbox . + +#ifndef SANDBOX_SRC_INTERCEPTION_AGENT_H__ +#define SANDBOX_SRC_INTERCEPTION_AGENT_H__ + +#include "base/basictypes.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +namespace sandbox { + +// Internal structures used for communication between the broker and the target. +struct DllInterceptionData; +struct SharedMemory; +struct DllPatchInfo; + +class ResolverThunk; + +// The InterceptionAgent executes on the target application, and it is in charge +// of setting up the desired interceptions or indicating what module needs to +// be unloaded. +// +// The exposed API consists of three methods: GetInterceptionAgent to retrieve +// the single class instance, OnDllLoad and OnDllUnload to process a dll being +// loaded and unloaded respectively. +// +// This class assumes that it will get called for every dll being loaded, +// starting with kernel32, so the singleton will be instantiated from within the +// loader lock. +class InterceptionAgent { + public: + // Returns the single InterceptionAgent object for this process. + static InterceptionAgent* GetInterceptionAgent(); + + // This method should be invoked whenever a new dll is loaded to perform the + // required patches. If the return value is false, this dll should not be + // allowed to load. + // + // full_path is the (optional) full name of the module being loaded and name + // is the internal module name. If full_path is provided, it will be used + // before the internal name to determine if we care about this dll. + bool OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name, + void* base_address); + + // Performs cleanup when a dll is unloaded. + void OnDllUnload(void* base_address); + + private: + ~InterceptionAgent() {} + + // Performs initialization of the singleton. + bool Init(SharedMemory* shared_memory); + + // Returns true if we are interested on this dll. dll_info is an entry of the + // list of intercepted dlls. + bool DllMatch(const UNICODE_STRING* full_path, const UNICODE_STRING* name, + const DllPatchInfo* dll_info); + + // Performs the patching of the dll loaded at base_address. + // The patches to perform are described on dll_info, and thunks is the thunk + // storage for the whole dll. + // Returns true on success. + bool PatchDll(const DllPatchInfo* dll_info, DllInterceptionData* thunks); + + // Returns a resolver for a given interception type. + ResolverThunk* GetResolver(InterceptionType type); + + // Shared memory containing the list of functions to intercept. + SharedMemory* interceptions_; + + // Array of thunk data buffers for the intercepted dlls. This object singleton + // is allocated with a placement new with enough space to hold the complete + // array of pointers, not just the first element. + DllInterceptionData* dlls_[1]; + + DISALLOW_IMPLICIT_CONSTRUCTORS(InterceptionAgent); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_INTERCEPTION_AGENT_H__ diff --git a/sandbox/win/src/interception_internal.h b/sandbox/win/src/interception_internal.h new file mode 100644 index 0000000..f3c401c --- /dev/null +++ b/sandbox/win/src/interception_internal.h @@ -0,0 +1,76 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines InterceptionManager, the class in charge of setting up interceptions +// for the sandboxed process. For more details see: +// http://dev.chromium.org/developers/design-documents/sandbox . + +#ifndef SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ +#define SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ + +#include "sandbox/src/sandbox_types.h" + +namespace sandbox { + +const int kMaxThunkDataBytes = 64; + +enum InterceptorId; + +// The following structures contain variable size fields at the end, and will be +// used to transfer information between two processes. In order to guarantee +// our ability to follow the chain of structures, the alignment should be fixed, +// hence this pragma. +#pragma pack(push, 4) + +// Structures for the shared memory that contains patching information +// for the InterceptionAgent. +// A single interception: +struct FunctionInfo { + size_t record_bytes; // rounded to sizeof(size_t) bytes + InterceptionType type; + InterceptorId id; + const void* interceptor_address; + char function[1]; // placeholder for null terminated name + // char interceptor[] // followed by the interceptor function +}; + +// A single dll: +struct DllPatchInfo { + size_t record_bytes; // rounded to sizeof(size_t) bytes + size_t offset_to_functions; + int num_functions; + bool unload_module; + wchar_t dll_name[1]; // placeholder for null terminated name + // FunctionInfo function_info[] // followed by the functions to intercept +}; + +// All interceptions: +struct SharedMemory { + int num_intercepted_dlls; + void* interceptor_base; + DllPatchInfo dll_list[1]; // placeholder for the list of dlls +}; + +// Dummy single thunk: +struct ThunkData { + char data[kMaxThunkDataBytes]; +}; + +// In-memory representation of the interceptions for a given dll: +struct DllInterceptionData { + size_t data_bytes; + size_t used_bytes; + void* base; + int num_thunks; +#if defined(_WIN64) + int dummy; // Improve alignment. +#endif + ThunkData thunks[1]; +}; + +#pragma pack(pop) + +} // namespace sandbox + +#endif // SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ diff --git a/sandbox/win/src/interception_unittest.cc b/sandbox/win/src/interception_unittest.cc new file mode 100644 index 0000000..a0dd98d --- /dev/null +++ b/sandbox/win/src/interception_unittest.cc @@ -0,0 +1,212 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains unit tests for InterceptionManager. +// The tests require private information so the whole interception.cc file is +// included from this file. + +#include + +#include "base/memory/scoped_ptr.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/interception_internal.h" +#include "sandbox/src/target_process.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +// Walks the settings buffer, verifying that the values make sense and counting +// objects. +// Arguments: +// buffer (in): the buffer to walk. +// size (in): buffer size +// num_dlls (out): count of the dlls on the buffer. +// num_function (out): count of intercepted functions. +// num_names (out): count of named interceptor functions. +void WalkBuffer(void* buffer, size_t size, int* num_dlls, int* num_functions, + int* num_names) { + ASSERT_TRUE(NULL != buffer); + ASSERT_TRUE(NULL != num_functions); + ASSERT_TRUE(NULL != num_names); + *num_dlls = *num_functions = *num_names = 0; + SharedMemory *memory = reinterpret_cast(buffer); + + ASSERT_GT(size, sizeof(SharedMemory)); + DllPatchInfo *dll = &memory->dll_list[0]; + + for (int i = 0; i < memory->num_intercepted_dlls; i++) { + ASSERT_NE(0u, wcslen(dll->dll_name)); + ASSERT_EQ(0u, dll->record_bytes % sizeof(size_t)); + ASSERT_EQ(0u, dll->offset_to_functions % sizeof(size_t)); + ASSERT_NE(0, dll->num_functions); + + FunctionInfo *function = reinterpret_cast( + reinterpret_cast(dll) + dll->offset_to_functions); + + for (int j = 0; j < dll->num_functions; j++) { + ASSERT_EQ(0u, function->record_bytes % sizeof(size_t)); + + char* name = function->function; + size_t length = strlen(name); + ASSERT_NE(0u, length); + name += length + 1; + + // look for overflows + ASSERT_GT(reinterpret_cast(buffer) + size, name + strlen(name)); + + // look for a named interceptor + if (strlen(name)) { + (*num_names)++; + EXPECT_TRUE(NULL == function->interceptor_address); + } else { + EXPECT_TRUE(NULL != function->interceptor_address); + } + + (*num_functions)++; + function = reinterpret_cast( + reinterpret_cast(function) + function->record_bytes); + } + + (*num_dlls)++; + dll = reinterpret_cast(reinterpret_cast(dll) + + dll->record_bytes); + } +} + +TEST(InterceptionManagerTest, BufferLayout1) { + wchar_t exe_name[MAX_PATH]; + ASSERT_NE(0u, GetModuleFileName(NULL, exe_name, MAX_PATH - 1)); + + TargetProcess *target = MakeTestTargetProcess(::GetCurrentProcess(), + ::GetModuleHandle(exe_name)); + + InterceptionManager interceptions(target, true); + + // Any pointer will do for a function pointer. + void* function = &interceptions; + + // We don't care about the interceptor id. + interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile", + INTERCEPTION_SERVICE_CALL, function, + OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx", + INTERCEPTION_SMART_SIDESTEP, function, + OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"user32.dll", "FindWindow", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateMutex", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg", + INTERCEPTION_EAT, "replacement", + OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtClose", + INTERCEPTION_SERVICE_CALL, function, + OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtOpenFile", + INTERCEPTION_SIDESTEP, function, + OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"some.dll", "Superfn", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", + INTERCEPTION_EAT, "a", OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", + INTERCEPTION_SIDESTEP, "ab", OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", + INTERCEPTION_EAT, "abc", OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"a.dll", "p", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"b.dll", + "TheIncredibleCallToSaveTheWorld", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"a.dll", "BIsLame", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + interceptions.AddToPatchedFunctions(L"a.dll", "ARules", + INTERCEPTION_EAT, function, OPEN_KEY_ID); + + // Verify that all interceptions were added + ASSERT_EQ(18, interceptions.interceptions_.size()); + + size_t buffer_size = interceptions.GetBufferSize(); + scoped_array local_buffer(new BYTE[buffer_size]); + + ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), + buffer_size)); + + // At this point, the interceptions should have been separated into two + // groups: one group with the local ("cold") interceptions, consisting of + // everything from ntdll and stuff set as INTRECEPTION_SERVICE_CALL, and + // another group with the interceptions belonging to dlls that will be "hot" + // patched on the client. The second group lives on local_buffer, and the + // first group remains on the list of interceptions (inside the object + // "interceptions"). There are 3 local interceptions (of ntdll); the + // other 15 have to be sent to the child to be performed "hot". + EXPECT_EQ(3, interceptions.interceptions_.size()); + + int num_dlls, num_functions, num_names; + WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions, + &num_names); + + // The 15 interceptions on the buffer (to the child) should be grouped on 6 + // dlls. Only four interceptions are using an explicit name for the + // interceptor function. + EXPECT_EQ(6, num_dlls); + EXPECT_EQ(15, num_functions); + EXPECT_EQ(4, num_names); +} + +TEST(InterceptionManagerTest, BufferLayout2) { + wchar_t exe_name[MAX_PATH]; + ASSERT_NE(0u, GetModuleFileName(NULL, exe_name, MAX_PATH - 1)); + + TargetProcess *target = MakeTestTargetProcess(::GetCurrentProcess(), + ::GetModuleHandle(exe_name)); + + InterceptionManager interceptions(target, true); + + // Any pointer will do for a function pointer. + void* function = &interceptions; + interceptions.AddToUnloadModules(L"some01.dll"); + // We don't care about the interceptor id. + interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile", + INTERCEPTION_SERVICE_CALL, function, + OPEN_FILE_ID); + interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx", + INTERCEPTION_EAT, function, OPEN_FILE_ID); + interceptions.AddToUnloadModules(L"some02.dll"); + interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx", + INTERCEPTION_SMART_SIDESTEP, function, + OPEN_FILE_ID); + // Verify that all interceptions were added + ASSERT_EQ(5, interceptions.interceptions_.size()); + + size_t buffer_size = interceptions.GetBufferSize(); + scoped_array local_buffer(new BYTE[buffer_size]); + + ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), + buffer_size)); + + // At this point, the interceptions should have been separated into two + // groups: one group with the local ("cold") interceptions, and another + // group with the interceptions belonging to dlls that will be "hot" + // patched on the client. The second group lives on local_buffer, and the + // first group remains on the list of interceptions, in this case just one. + EXPECT_EQ(1, interceptions.interceptions_.size()); + + int num_dlls, num_functions, num_names; + WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions, + &num_names); + + EXPECT_EQ(3, num_dlls); + EXPECT_EQ(4, num_functions); + EXPECT_EQ(0, num_names); +} + +} // namespace sandbox diff --git a/sandbox/win/src/interceptors.h b/sandbox/win/src/interceptors.h new file mode 100644 index 0000000..67b0900 --- /dev/null +++ b/sandbox/win/src/interceptors.h @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_INTERCEPTORS_H_ +#define SANDBOX_SRC_INTERCEPTORS_H_ + +#if defined(_WIN64) +#include "sandbox/src/interceptors_64.h" +#endif + +namespace sandbox { + +enum InterceptorId { + // Internal use: + MAP_VIEW_OF_SECTION_ID = 0, + UNMAP_VIEW_OF_SECTION_ID, + // Policy broker: + SET_INFORMATION_THREAD_ID, + OPEN_THREAD_TOKEN_ID, + OPEN_THREAD_TOKEN_EX_ID, + OPEN_TREAD_ID, + OPEN_PROCESS_ID, + OPEN_PROCESS_TOKEN_ID, + OPEN_PROCESS_TOKEN_EX_ID, + // Filesystem dispatcher: + CREATE_FILE_ID, + OPEN_FILE_ID, + QUERY_ATTRIB_FILE_ID, + QUERY_FULL_ATTRIB_FILE_ID, + SET_INFO_FILE_ID, + // Named pipe dispatcher: + CREATE_NAMED_PIPE_ID, + // Process-thread dispatcher: + CREATE_PROCESSW_ID, + CREATE_PROCESSA_ID, + // Registry dispatcher: + CREATE_KEY_ID, + OPEN_KEY_ID, + OPEN_KEY_EX_ID, + // Sync dispatcher: + CREATE_EVENT_ID, + OPEN_EVENT_ID, + // CSRSS bypasses for HandleCloser: + CREATE_THREAD_ID, + GET_USER_DEFAULT_LCID_ID, + INTERCEPTOR_MAX_ID +}; + +typedef void* OriginalFunctions[INTERCEPTOR_MAX_ID]; + +} // namespace sandbox + +#endif // SANDBOX_SRC_INTERCEPTORS_H_ diff --git a/sandbox/win/src/interceptors_64.cc b/sandbox/win/src/interceptors_64.cc new file mode 100644 index 0000000..c068010 --- /dev/null +++ b/sandbox/win/src/interceptors_64.cc @@ -0,0 +1,268 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/interceptors_64.h" + +#include "sandbox/src/interceptors.h" +#include "sandbox/src/filesystem_interception.h" +#include "sandbox/src/named_pipe_interception.h" +#include "sandbox/src/policy_target.h" +#include "sandbox/src/process_thread_interception.h" +#include "sandbox/src/registry_interception.h" +#include "sandbox/src/sandbox_nt_types.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/sync_interception.h" +#include "sandbox/src/target_interceptions.h" + +namespace sandbox { + +SANDBOX_INTERCEPT NtExports g_nt; +SANDBOX_INTERCEPT OriginalFunctions g_originals; + +NTSTATUS WINAPI TargetNtMapViewOfSection64( + HANDLE section, HANDLE process, PVOID *base, ULONG_PTR zero_bits, + SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size, + SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect) { + NtMapViewOfSectionFunction orig_fn = reinterpret_cast< + NtMapViewOfSectionFunction>(g_originals[MAP_VIEW_OF_SECTION_ID]); + + return TargetNtMapViewOfSection(orig_fn, section, process, base, zero_bits, + commit_size, offset, view_size, inherit, + allocation_type, protect); +} + +NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process, PVOID base) { + NtUnmapViewOfSectionFunction orig_fn = reinterpret_cast< + NtUnmapViewOfSectionFunction>(g_originals[UNMAP_VIEW_OF_SECTION_ID]); + return TargetNtUnmapViewOfSection(orig_fn, process, base); +} + +// ----------------------------------------------------------------------- + +NTSTATUS WINAPI TargetNtSetInformationThread64( + HANDLE thread, NT_THREAD_INFORMATION_CLASS thread_info_class, + PVOID thread_information, ULONG thread_information_bytes) { + NtSetInformationThreadFunction orig_fn = reinterpret_cast< + NtSetInformationThreadFunction>(g_originals[SET_INFORMATION_THREAD_ID]); + return TargetNtSetInformationThread(orig_fn, thread, thread_info_class, + thread_information, + thread_information_bytes); +} + +NTSTATUS WINAPI TargetNtOpenThreadToken64( + HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self, + PHANDLE token) { + NtOpenThreadTokenFunction orig_fn = reinterpret_cast< + NtOpenThreadTokenFunction>(g_originals[OPEN_THREAD_TOKEN_ID]); + return TargetNtOpenThreadToken(orig_fn, thread, desired_access, open_as_self, + token); +} + +NTSTATUS WINAPI TargetNtOpenThreadTokenEx64( + HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self, + ULONG handle_attributes, PHANDLE token) { + NtOpenThreadTokenExFunction orig_fn = reinterpret_cast< + NtOpenThreadTokenExFunction>(g_originals[OPEN_THREAD_TOKEN_EX_ID]); + return TargetNtOpenThreadTokenEx(orig_fn, thread, desired_access, + open_as_self, handle_attributes, token); +} + +HANDLE WINAPI TargetCreateThread64( + LPSECURITY_ATTRIBUTES thread_attributes, SIZE_T stack_size, + LPTHREAD_START_ROUTINE start_address, PVOID parameter, DWORD creation_flags, + LPDWORD thread_id) { + CreateThreadFunction orig_fn = reinterpret_cast< + CreateThreadFunction>(g_originals[CREATE_THREAD_ID]); + return TargetCreateThread(orig_fn, thread_attributes, stack_size, + start_address, parameter, creation_flags, + thread_id); +} + +LCID WINAPI TargetGetUserDefaultLCID64(void) { + GetUserDefaultLCIDFunction orig_fn = reinterpret_cast< + GetUserDefaultLCIDFunction>(g_originals[GET_USER_DEFAULT_LCID_ID]); + return TargetGetUserDefaultLCID(orig_fn); +} + +// ----------------------------------------------------------------------- + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateFile64( + PHANDLE file, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, + PLARGE_INTEGER allocation_size, ULONG file_attributes, ULONG sharing, + ULONG disposition, ULONG options, PVOID ea_buffer, ULONG ea_length) { + NtCreateFileFunction orig_fn = reinterpret_cast< + NtCreateFileFunction>(g_originals[CREATE_FILE_ID]); + return TargetNtCreateFile(orig_fn, file, desired_access, object_attributes, + io_status, allocation_size, file_attributes, + sharing, disposition, options, ea_buffer, + ea_length); +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenFile64( + PHANDLE file, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, + ULONG sharing, ULONG options) { + NtOpenFileFunction orig_fn = reinterpret_cast< + NtOpenFileFunction>(g_originals[OPEN_FILE_ID]); + return TargetNtOpenFile(orig_fn, file, desired_access, object_attributes, + io_status, sharing, options); +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryAttributesFile64( + POBJECT_ATTRIBUTES object_attributes, + PFILE_BASIC_INFORMATION file_attributes) { + NtQueryAttributesFileFunction orig_fn = reinterpret_cast< + NtQueryAttributesFileFunction>(g_originals[QUERY_ATTRIB_FILE_ID]); + return TargetNtQueryAttributesFile(orig_fn, object_attributes, + file_attributes); +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64( + POBJECT_ATTRIBUTES object_attributes, + PFILE_NETWORK_OPEN_INFORMATION file_attributes) { + NtQueryFullAttributesFileFunction orig_fn = reinterpret_cast< + NtQueryFullAttributesFileFunction>( + g_originals[QUERY_FULL_ATTRIB_FILE_ID]); + return TargetNtQueryFullAttributesFile(orig_fn, object_attributes, + file_attributes); +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationFile64( + HANDLE file, PIO_STATUS_BLOCK io_status, PVOID file_information, + ULONG length, FILE_INFORMATION_CLASS file_information_class) { + NtSetInformationFileFunction orig_fn = reinterpret_cast< + NtSetInformationFileFunction>(g_originals[SET_INFO_FILE_ID]); + return TargetNtSetInformationFile(orig_fn, file, io_status, file_information, + length, file_information_class); +} + +// ----------------------------------------------------------------------- + +SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateNamedPipeW64( + LPCWSTR pipe_name, DWORD open_mode, DWORD pipe_mode, DWORD max_instance, + DWORD out_buffer_size, DWORD in_buffer_size, DWORD default_timeout, + LPSECURITY_ATTRIBUTES security_attributes) { + CreateNamedPipeWFunction orig_fn = reinterpret_cast< + CreateNamedPipeWFunction>(g_originals[CREATE_NAMED_PIPE_ID]); + return TargetCreateNamedPipeW(orig_fn, pipe_name, open_mode, pipe_mode, + max_instance, out_buffer_size, in_buffer_size, + default_timeout, security_attributes); +} + +// ----------------------------------------------------------------------- + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThread64( + PHANDLE thread, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id) { + NtOpenThreadFunction orig_fn = reinterpret_cast< + NtOpenThreadFunction>(g_originals[OPEN_TREAD_ID]); + return TargetNtOpenThread(orig_fn, thread, desired_access, object_attributes, + client_id); +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcess64( + PHANDLE process, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id) { + NtOpenProcessFunction orig_fn = reinterpret_cast< + NtOpenProcessFunction>(g_originals[OPEN_PROCESS_ID]); + return TargetNtOpenProcess(orig_fn, process, desired_access, + object_attributes, client_id); +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessToken64( + HANDLE process, ACCESS_MASK desired_access, PHANDLE token) { + NtOpenProcessTokenFunction orig_fn = reinterpret_cast< + NtOpenProcessTokenFunction>(g_originals[OPEN_PROCESS_TOKEN_ID]); + return TargetNtOpenProcessToken(orig_fn, process, desired_access, token); +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessTokenEx64( + HANDLE process, ACCESS_MASK desired_access, ULONG handle_attributes, + PHANDLE token) { + NtOpenProcessTokenExFunction orig_fn = reinterpret_cast< + NtOpenProcessTokenExFunction>(g_originals[OPEN_PROCESS_TOKEN_EX_ID]); + return TargetNtOpenProcessTokenEx(orig_fn, process, desired_access, + handle_attributes, token); +} + +SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessW64( + LPCWSTR application_name, LPWSTR command_line, + LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, + LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info, + LPPROCESS_INFORMATION process_information) { + CreateProcessWFunction orig_fn = reinterpret_cast< + CreateProcessWFunction>(g_originals[CREATE_PROCESSW_ID]); + return TargetCreateProcessW(orig_fn, application_name, command_line, + process_attributes, thread_attributes, + inherit_handles, flags, environment, + current_directory, startup_info, + process_information); +} + +SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA64( + LPCSTR application_name, LPSTR command_line, + LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, + LPVOID environment, LPCSTR current_directory, LPSTARTUPINFOA startup_info, + LPPROCESS_INFORMATION process_information) { + CreateProcessAFunction orig_fn = reinterpret_cast< + CreateProcessAFunction>(g_originals[CREATE_PROCESSA_ID]); + return TargetCreateProcessA(orig_fn, application_name, command_line, + process_attributes, thread_attributes, + inherit_handles, flags, environment, + current_directory, startup_info, + process_information); +} + +// ----------------------------------------------------------------------- + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey64( + PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, ULONG title_index, + PUNICODE_STRING class_name, ULONG create_options, PULONG disposition) { + NtCreateKeyFunction orig_fn = reinterpret_cast< + NtCreateKeyFunction>(g_originals[CREATE_KEY_ID]); + return TargetNtCreateKey(orig_fn, key, desired_access, object_attributes, + title_index, class_name, create_options, + disposition); +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey64( + PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes) { + NtOpenKeyFunction orig_fn = reinterpret_cast< + NtOpenKeyFunction>(g_originals[OPEN_KEY_ID]); + return TargetNtOpenKey(orig_fn, key, desired_access, object_attributes); +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx64( + PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, ULONG open_options) { + NtOpenKeyExFunction orig_fn = reinterpret_cast< + NtOpenKeyExFunction>(g_originals[OPEN_KEY_EX_ID]); + return TargetNtOpenKeyEx(orig_fn, key, desired_access, object_attributes, + open_options); +} + +// ----------------------------------------------------------------------- + +SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW64( + LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset, + BOOL initial_state, LPCWSTR name) { + CreateEventWFunction orig_fn = reinterpret_cast< + CreateEventWFunction>(g_originals[CREATE_EVENT_ID]); + return TargetCreateEventW(orig_fn, security_attributes, manual_reset, + initial_state, name); +} + +SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW64( + ACCESS_MASK desired_access, BOOL inherit_handle, LPCWSTR name) { + OpenEventWFunction orig_fn = reinterpret_cast< + OpenEventWFunction>(g_originals[OPEN_EVENT_ID]); + return TargetOpenEventW(orig_fn, desired_access, inherit_handle, name); +} + +} // namespace sandbox diff --git a/sandbox/win/src/interceptors_64.h b/sandbox/win/src/interceptors_64.h new file mode 100644 index 0000000..50355a0 --- /dev/null +++ b/sandbox/win/src/interceptors_64.h @@ -0,0 +1,169 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +#ifndef SANDBOX_SRC_INTERCEPTORS_64_H_ +#define SANDBOX_SRC_INTERCEPTORS_64_H_ + +namespace sandbox { + +extern "C" { + +// Interception of NtMapViewOfSection on the child process. +// It should never be called directly. This function provides the means to +// detect dlls being loaded, so we can patch them if needed. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtMapViewOfSection64( + HANDLE section, HANDLE process, PVOID *base, ULONG_PTR zero_bits, + SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size, + SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect); + +// Interception of NtUnmapViewOfSection on the child process. +// It should never be called directly. This function provides the means to +// detect dlls being unloaded, so we can clean up our interceptions. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process, + PVOID base); + +// ----------------------------------------------------------------------- +// Interceptors without IPC. + +// Interception of NtSetInformationThread on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationThread64( + HANDLE thread, NT_THREAD_INFORMATION_CLASS thread_info_class, + PVOID thread_information, ULONG thread_information_bytes); + +// Interception of NtOpenThreadToken on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadToken64( + HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self, + PHANDLE token); + +// Interception of NtOpenThreadTokenEx on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadTokenEx64( + HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self, + ULONG handle_attributes, PHANDLE token); + +// Interception of CreateThread on the child process. +SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateThread64( + LPSECURITY_ATTRIBUTES thread_attributes, SIZE_T stack_size, + LPTHREAD_START_ROUTINE start_address, PVOID parameter, + DWORD creation_flags, LPDWORD thread_id); + +// Interception of GetUserDefaultLCID on the child process. +SANDBOX_INTERCEPT LCID WINAPI TargetGetUserDefaultLCID64(); + +// ----------------------------------------------------------------------- +// Interceptors handled by the file system dispatcher. + +// Interception of NtCreateFile on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateFile64( + PHANDLE file, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, + PLARGE_INTEGER allocation_size, ULONG file_attributes, ULONG sharing, + ULONG disposition, ULONG options, PVOID ea_buffer, ULONG ea_length); + +// Interception of NtOpenFile on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenFile64( + PHANDLE file, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, + ULONG sharing, ULONG options); + +// Interception of NtQueryAtttributesFile on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryAttributesFile64( + POBJECT_ATTRIBUTES object_attributes, + PFILE_BASIC_INFORMATION file_attributes); + +// Interception of NtQueryFullAtttributesFile on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64( + POBJECT_ATTRIBUTES object_attributes, + PFILE_NETWORK_OPEN_INFORMATION file_attributes); + +// Interception of NtSetInformationFile on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationFile64( + HANDLE file, PIO_STATUS_BLOCK io_status, PVOID file_information, + ULONG length, FILE_INFORMATION_CLASS file_information_class); + +// ----------------------------------------------------------------------- +// Interceptors handled by the named pipe dispatcher. + +// Interception of CreateNamedPipeW in kernel32.dll +SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateNamedPipeW64( + LPCWSTR pipe_name, DWORD open_mode, DWORD pipe_mode, DWORD max_instance, + DWORD out_buffer_size, DWORD in_buffer_size, DWORD default_timeout, + LPSECURITY_ATTRIBUTES security_attributes); + +// ----------------------------------------------------------------------- +// Interceptors handled by the process-thread dispatcher. + +// Interception of NtOpenThread on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThread64( + PHANDLE thread, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id); + +// Interception of NtOpenProcess on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcess64( + PHANDLE process, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id); + +// Interception of NtOpenProcessToken on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessToken64( + HANDLE process, ACCESS_MASK desired_access, PHANDLE token); + +// Interception of NtOpenProcessTokenEx on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessTokenEx64( + HANDLE process, ACCESS_MASK desired_access, ULONG handle_attributes, + PHANDLE token); + +// Interception of CreateProcessW in kernel32.dll. +SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessW64( + LPCWSTR application_name, LPWSTR command_line, + LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, + LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info, + LPPROCESS_INFORMATION process_information); + +// Interception of CreateProcessA in kernel32.dll. +SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA64( + LPCSTR application_name, LPSTR command_line, + LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, + LPVOID environment, LPCSTR current_directory, LPSTARTUPINFOA startup_info, + LPPROCESS_INFORMATION process_information); + +// ----------------------------------------------------------------------- +// Interceptors handled by the registry dispatcher. + +// Interception of NtCreateKey on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey64( + PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, ULONG title_index, + PUNICODE_STRING class_name, ULONG create_options, PULONG disposition); + +// Interception of NtOpenKey on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey64( + PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes); + +// Interception of NtOpenKeyEx on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx64( + PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, ULONG open_options); + +// ----------------------------------------------------------------------- +// Interceptors handled by the sync dispatcher. + +// Interception of CreateEventW on the child process. +SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW64( + LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset, + BOOL initial_state, LPCWSTR name); + +// Interception of OpenEventW on the child process. +SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW64( + ACCESS_MASK desired_access, BOOL inherit_handle, LPCWSTR name); + +} // extern "C" + +} // namespace sandbox + +#endif // SANDBOX_SRC_INTERCEPTORS_64_H_ diff --git a/sandbox/win/src/internal_types.h b/sandbox/win/src/internal_types.h new file mode 100644 index 0000000..db969aa --- /dev/null +++ b/sandbox/win/src/internal_types.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_INTERNAL_TYPES_H_ +#define SANDBOX_SRC_INTERNAL_TYPES_H_ + +namespace sandbox { + +const wchar_t kNtdllName[] = L"ntdll.dll"; +const wchar_t kKerneldllName[] = L"kernel32.dll"; + +// Defines the supported C++ types encoding to numeric id. Like a poor's man +// RTTI. Note that true C++ RTTI will not work because the types are not +// polymorphic anyway. +enum ArgType { + INVALID_TYPE = 0, + WCHAR_TYPE, + ULONG_TYPE, + UNISTR_TYPE, + VOIDPTR_TYPE, + INPTR_TYPE, + INOUTPTR_TYPE, + LAST_TYPE +}; + +// Encapsulates a pointer to a buffer and the size of the buffer. +class CountedBuffer { + public: + CountedBuffer(void* buffer, uint32 size) : size_(size), buffer_(buffer) {} + + uint32 Size() const { + return size_; + } + + void* Buffer() const { + return buffer_; + } + + private: + uint32 size_; + void* buffer_; +}; + +// Helper class to convert void-pointer packed ints for both +// 32 and 64 bit builds. This construct is non-portable. +class IPCInt { + public: + explicit IPCInt(void* buffer) { + buffer_.vp = buffer; + } + + explicit IPCInt(unsigned __int32 i32) { + buffer_.vp = NULL; + buffer_.i32 = i32; + } + + unsigned __int32 As32Bit() const { + return buffer_.i32; + } + + void* AsVoidPtr() const { + return buffer_.vp; + } + + private: + union U { + void* vp; + unsigned __int32 i32; + } buffer_; +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_INTERNAL_TYPES_H_ diff --git a/sandbox/win/src/ipc_ping_test.cc b/sandbox/win/src/ipc_ping_test.cc new file mode 100644 index 0000000..410fd6a --- /dev/null +++ b/sandbox/win/src/ipc_ping_test.cc @@ -0,0 +1,58 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/target_services.h" +#include "sandbox/tests/common/controller.h" + +namespace sandbox { + +// Tests that the IPC is working by issuing a special IPC that is not exposed +// in the public API. +SBOX_TESTS_COMMAND int IPC_Ping(int argc, wchar_t **argv) { + if (argc != 1) + return SBOX_TEST_FAILED; + + TargetServices* ts = SandboxFactory::GetTargetServices(); + if (NULL == ts) + return SBOX_TEST_FAILED; + + // Downcast because we have internal knowledge of the object returned. + TargetServicesBase* ts_base = reinterpret_cast(ts); + + int version = 0; + if (L'1' == argv[0][0]) + version = 1; + else + version = 2; + + if (!ts_base->TestIPCPing(version)) + return SBOX_TEST_FAILED; + + ::Sleep(1); + if (!ts_base->TestIPCPing(version)) + return SBOX_TEST_FAILED; + + return SBOX_TEST_SUCCEEDED; +} + +// The IPC ping test should work before and after the token drop. +TEST(IPCTest, IPCPingTestSimple) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(EVERY_STATE); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 1")); +} + +TEST(IPCTest, IPCPingTestWithOutput) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(EVERY_STATE); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2")); +} + +} // namespace sandbox diff --git a/sandbox/win/src/ipc_tags.h b/sandbox/win/src/ipc_tags.h new file mode 100644 index 0000000..4e3a806 --- /dev/null +++ b/sandbox/win/src/ipc_tags.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_IPC_TAGS_H__ +#define SANDBOX_SRC_IPC_TAGS_H__ + +namespace sandbox { + +enum { + IPC_UNUSED_TAG = 0, + IPC_PING1_TAG, // Takes a cookie in parameters and returns the cookie + // multiplied by 2 and the tick_count. Used for testing only. + IPC_PING2_TAG, // Takes an in/out cookie in parameters and modify the cookie + // to be multiplied by 3. Used for testing only. + IPC_NTCREATEFILE_TAG, + IPC_NTOPENFILE_TAG, + IPC_NTQUERYATTRIBUTESFILE_TAG, + IPC_NTQUERYFULLATTRIBUTESFILE_TAG, + IPC_NTSETINFO_RENAME_TAG, + IPC_CREATENAMEDPIPEW_TAG, + IPC_NTOPENTHREAD_TAG, + IPC_NTOPENPROCESS_TAG, + IPC_NTOPENPROCESSTOKEN_TAG, + IPC_NTOPENPROCESSTOKENEX_TAG, + IPC_CREATEPROCESSW_TAG, + IPC_CREATEEVENT_TAG, + IPC_OPENEVENT_TAG, + IPC_NTCREATEKEY_TAG, + IPC_NTOPENKEY_TAG, + IPC_DUPLICATEHANDLEPROXY_TAG, + IPC_LAST_TAG +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_IPC_TAGS_H__ diff --git a/sandbox/win/src/ipc_unittest.cc b/sandbox/win/src/ipc_unittest.cc new file mode 100644 index 0000000..e1fb7c1 --- /dev/null +++ b/sandbox/win/src/ipc_unittest.cc @@ -0,0 +1,641 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/sharedmem_ipc_server.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +// Helper function to make the fake shared memory with some +// basic elements initialized. +IPCControl* MakeChannels(size_t channel_size, size_t total_shared_size, + size_t* base_start) { + // Allocate memory + char* mem = new char[total_shared_size]; + memset(mem, 0, total_shared_size); + // Calculate how many channels we can fit in the shared memory. + total_shared_size -= offsetof(IPCControl, channels); + size_t channel_count = + total_shared_size / (sizeof(ChannelControl) + channel_size); + // Calculate the start of the first channel. + *base_start = (sizeof(ChannelControl)* channel_count) + + offsetof(IPCControl, channels); + // Setup client structure. + IPCControl* client_control = reinterpret_cast(mem); + client_control->channels_count = channel_count; + return client_control; +} + +enum TestFixMode { + FIX_NO_EVENTS, + FIX_PONG_READY, + FIX_PONG_NOT_READY +}; + +void FixChannels(IPCControl* client_control, size_t base_start, + size_t channel_size, TestFixMode mode) { + for (size_t ix = 0; ix != client_control->channels_count; ++ix) { + ChannelControl& channel = client_control->channels[ix]; + channel.channel_base = base_start; + channel.state = kFreeChannel; + if (mode != FIX_NO_EVENTS) { + BOOL signaled = (FIX_PONG_READY == mode)? TRUE : FALSE; + channel.ping_event = ::CreateEventW(NULL, FALSE, FALSE, NULL); + channel.pong_event = ::CreateEventW(NULL, FALSE, signaled, NULL); + } + base_start += channel_size; + } +} + +void CloseChannelEvents(IPCControl* client_control) { + for (size_t ix = 0; ix != client_control->channels_count; ++ix) { + ChannelControl& channel = client_control->channels[ix]; + ::CloseHandle(channel.ping_event); + ::CloseHandle(channel.pong_event); + } +} + +TEST(IPCTest, ChannelMaker) { + // Test that our testing rig is computing offsets properly. We should have + // 5 channnels and the offset to the first channel is 108 bytes in 32 bits + // and 216 in 64 bits. + size_t channel_start = 0; + IPCControl* client_control = MakeChannels(12 * 64, 4096, &channel_start); + ASSERT_TRUE(NULL != client_control); + EXPECT_EQ(5, client_control->channels_count); +#if defined(_WIN64) + EXPECT_EQ(216, channel_start); +#else + EXPECT_EQ(108, channel_start); +#endif + delete[] reinterpret_cast(client_control); +} + +TEST(IPCTest, ClientLockUnlock) { + // Make 7 channels of kIPCChannelSize (1kb) each. Test that we lock and + // unlock channels properly. + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096 * 2, &base_start); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_NO_EVENTS); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + // Test that we lock the first 3 channels in sequence. + void* buff0 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + void* buff1 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + void* buff2 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[2].channel_base == buff2); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kBusyChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + // Test that we unlock and re-lock the right channel. + client.FreeBuffer(buff1); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kBusyChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + void* buff2b = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff2b); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kBusyChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + client.FreeBuffer(buff0); + EXPECT_EQ(kFreeChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kBusyChannel, client_control->channels[2].state); + EXPECT_EQ(kFreeChannel, client_control->channels[3].state); + EXPECT_EQ(kFreeChannel, client_control->channels[4].state); + EXPECT_EQ(kFreeChannel, client_control->channels[5].state); + + delete[] reinterpret_cast(client_control); +} + +TEST(IPCTest, CrossCallStrPacking) { + // This test tries the CrossCall object with null and non-null string + // combination of parameters, integer types and verifies that the unpacker + // can read them properly. + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096 * 4, &base_start); + client_control->server_alive = HANDLE(1); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + CrossCallReturn answer; + uint32 tag1 = 666; + const wchar_t text[] = L"98765 - 43210"; + std::wstring copied_text; + CrossCallParamsEx* actual_params; + + CrossCall(client, tag1, text, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(1, actual_params->GetParamsCount()); + EXPECT_EQ(tag1, actual_params->GetTag()); + EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); + EXPECT_STREQ(text, copied_text.c_str()); + + // Check with an empty string. + uint32 tag2 = 777; + const wchar_t* null_text = NULL; + CrossCall(client, tag2, null_text, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(1, actual_params->GetParamsCount()); + EXPECT_EQ(tag2, actual_params->GetTag()); + uint32 param_size = 1; + ArgType type = INVALID_TYPE; + void* param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); + EXPECT_TRUE(NULL != param_addr); + EXPECT_EQ(0, param_size); + EXPECT_EQ(WCHAR_TYPE, type); + EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); + + uint32 tag3 = 888; + param_size = 1; + copied_text.clear(); + + // Check with an empty string and a non-empty string. + CrossCall(client, tag3, null_text, text, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(2, actual_params->GetParamsCount()); + EXPECT_EQ(tag3, actual_params->GetTag()); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); + EXPECT_TRUE(NULL != param_addr); + EXPECT_EQ(0, param_size); + EXPECT_EQ(WCHAR_TYPE, type); + EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); + EXPECT_TRUE(actual_params->GetParameterStr(1, &copied_text)); + EXPECT_STREQ(text, copied_text.c_str()); + + param_size = 1; + std::wstring copied_text_p0, copied_text_p2; + + const wchar_t text2[] = L"AeFG"; + CrossCall(client, tag1, text2, null_text, text, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(3, actual_params->GetParamsCount()); + EXPECT_EQ(tag1, actual_params->GetTag()); + EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text_p0)); + EXPECT_STREQ(text2, copied_text_p0.c_str()); + EXPECT_TRUE(actual_params->GetParameterStr(2, &copied_text_p2)); + EXPECT_STREQ(text, copied_text_p2.c_str()); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); + EXPECT_TRUE(NULL != param_addr); + EXPECT_EQ(0, param_size); + EXPECT_EQ(WCHAR_TYPE, type); + + CloseChannelEvents(client_control); + delete[] reinterpret_cast(client_control); +} + +TEST(IPCTest, CrossCallIntPacking) { + // Check handling for regular 32 bit integers used in Windows. + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096 * 4, &base_start); + client_control->server_alive = HANDLE(1); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); + + uint32 tag1 = 999; + uint32 tag2 = 111; + const wchar_t text[] = L"godzilla"; + CrossCallParamsEx* actual_params; + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + CrossCallReturn answer; + DWORD dw = 0xE6578; + CrossCall(client, tag2, dw, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(1, actual_params->GetParamsCount()); + EXPECT_EQ(tag2, actual_params->GetTag()); + ArgType type = INVALID_TYPE; + uint32 param_size = 1; + void* param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); + ASSERT_EQ(sizeof(dw), param_size); + EXPECT_EQ(ULONG_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&dw, param_addr, param_size)); + + // Check handling for windows HANDLES. + HANDLE h = HANDLE(0x70000500); + CrossCall(client, tag1, text, h, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(2, actual_params->GetParamsCount()); + EXPECT_EQ(tag1, actual_params->GetTag()); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); + ASSERT_EQ(sizeof(h), param_size); + EXPECT_EQ(VOIDPTR_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&h, param_addr, param_size)); + + // Check combination of 32 and 64 bits. + CrossCall(client, tag2, h, dw, h, &answer); + actual_params = reinterpret_cast(client.GetBuffer()); + EXPECT_EQ(3, actual_params->GetParamsCount()); + EXPECT_EQ(tag2, actual_params->GetTag()); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); + ASSERT_EQ(sizeof(h), param_size); + EXPECT_EQ(VOIDPTR_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&h, param_addr, param_size)); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); + ASSERT_EQ(sizeof(dw), param_size); + EXPECT_EQ(ULONG_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&dw, param_addr, param_size)); + type = INVALID_TYPE; + param_addr = actual_params->GetRawParameter(2, ¶m_size, &type); + ASSERT_EQ(sizeof(h), param_size); + EXPECT_EQ(VOIDPTR_TYPE, type); + ASSERT_TRUE(NULL != param_addr); + EXPECT_EQ(0, memcmp(&h, param_addr, param_size)); + + CloseChannelEvents(client_control); + delete[] reinterpret_cast(client_control); +} + +TEST(IPCTest, CrossCallValidation) { + // First a sanity test with a well formed parameter object. + unsigned long value = 124816; + const uint32 kTag = 33; + const uint32 kBufferSize = 256; + ActualCallParams<1, kBufferSize> params_1(kTag); + params_1.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); + void* buffer = const_cast(params_1.GetBuffer()); + + uint32 out_size = 0; + CrossCallParamsEx* ccp = 0; + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(), + &out_size); + ASSERT_TRUE(NULL != ccp); + EXPECT_TRUE(ccp->GetBuffer() != buffer); + EXPECT_EQ(kTag, ccp->GetTag()); + EXPECT_EQ(1, ccp->GetParamsCount()); + delete[] (reinterpret_cast(ccp)); + +#if defined(NDEBUG) + // Test hat we handle integer overflow on the number of params + // correctly. We use a test-only ctor for ActualCallParams that + // allows to create malformed cross-call buffers. + const int32 kPtrDiffSz = sizeof(ptrdiff_t); + for (int32 ix = -1; ix != 3; ++ix) { + uint32 fake_num_params = (kuint32max / kPtrDiffSz) + ix; + ActualCallParams<1, kBufferSize> params_2(kTag, fake_num_params); + params_2.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); + buffer = const_cast(params_2.GetBuffer()); + + EXPECT_TRUE(NULL != buffer); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(), + &out_size); + // If the buffer is malformed the return is NULL. + EXPECT_TRUE(NULL == ccp); + } +#endif // defined(NDEBUG) + + ActualCallParams<1, kBufferSize> params_3(kTag, 1); + params_3.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); + buffer = const_cast(params_3.GetBuffer()); + EXPECT_TRUE(NULL != buffer); + + uint32 correct_size = params_3.OverrideSize(1); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL == ccp); + + // The correct_size is 8 bytes aligned. + params_3.OverrideSize(correct_size - 7); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL == ccp); + + params_3.OverrideSize(correct_size); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL != ccp); + + // Make sure that two parameters work as expected. + ActualCallParams<2, kBufferSize> params_4(kTag, 2); + params_4.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE); + params_4.CopyParamIn(1, buffer, sizeof(buffer), false, VOIDPTR_TYPE); + buffer = const_cast(params_4.GetBuffer()); + EXPECT_TRUE(NULL != buffer); + + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL != ccp); + +#if defined(_WIN64) + correct_size = params_4.OverrideSize(1); + params_4.OverrideSize(correct_size - 1); + ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); + EXPECT_TRUE(NULL == ccp); +#endif +} + +// This structure is passed to the mock server threads to simulate +// the server side IPC so it has the required kernel objects. +struct ServerEvents { + HANDLE ping; + HANDLE pong; + volatile LONG* state; + HANDLE mutex; +}; + +// This is the server thread that quicky answers an IPC and exits. +DWORD WINAPI QuickResponseServer(PVOID param) { + ServerEvents* events = reinterpret_cast(param); + DWORD wait_result = 0; + wait_result = ::WaitForSingleObject(events->ping, INFINITE); + ::InterlockedExchange(events->state, kAckChannel); + ::SetEvent(events->pong); + return wait_result; +} + +class CrossCallParamsMock : public CrossCallParams { + public: + CrossCallParamsMock(uint32 tag, uint32 params_count) + : CrossCallParams(tag, params_count) { + } + private: + void* params[4]; +}; + +void FakeOkAnswerInChannel(void* channel) { + CrossCallReturn* answer = reinterpret_cast(channel); + answer->call_outcome = SBOX_ALL_OK; +} + +// Create two threads that will quickly answer IPCs; the first one +// using channel 1 (channel 0 is busy) and one using channel 0. No time-out +// should occur. +TEST(IPCTest, ClientFastServer) { + const size_t channel_size = kIPCChannelSize; + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(channel_size, 4096 * 2, &base_start); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY); + client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + ServerEvents events = {0}; + events.ping = client_control->channels[1].ping_event; + events.pong = client_control->channels[1].pong_event; + events.state = &client_control->channels[1].state; + + HANDLE t1 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL); + ASSERT_TRUE(NULL != t1); + ::CloseHandle(t1); + + void* buff0 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + + void* buff1 = client.GetBuffer(); + EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kBusyChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + + EXPECT_EQ(0, client_control->channels[1].ipc_tag); + + uint32 tag = 7654; + CrossCallReturn answer; + CrossCallParamsMock* params1 = new(buff1) CrossCallParamsMock(tag, 1); + FakeOkAnswerInChannel(buff1); + + ResultCode result = client.DoCall(params1, &answer); + if (SBOX_ERROR_CHANNEL_ERROR != result) + client.FreeBuffer(buff1); + + EXPECT_TRUE(SBOX_ALL_OK == result); + EXPECT_EQ(tag, client_control->channels[1].ipc_tag); + EXPECT_EQ(kBusyChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + + HANDLE t2 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL); + ASSERT_TRUE(NULL != t2); + ::CloseHandle(t2); + + client.FreeBuffer(buff0); + events.ping = client_control->channels[0].ping_event; + events.pong = client_control->channels[0].pong_event; + events.state = &client_control->channels[0].state; + + tag = 4567; + CrossCallParamsMock* params2 = new(buff0) CrossCallParamsMock(tag, 1); + FakeOkAnswerInChannel(buff0); + + result = client.DoCall(params2, &answer); + if (SBOX_ERROR_CHANNEL_ERROR != result) + client.FreeBuffer(buff0); + + EXPECT_TRUE(SBOX_ALL_OK == result); + EXPECT_EQ(tag, client_control->channels[0].ipc_tag); + EXPECT_EQ(kFreeChannel, client_control->channels[0].state); + EXPECT_EQ(kFreeChannel, client_control->channels[1].state); + EXPECT_EQ(kFreeChannel, client_control->channels[2].state); + + CloseChannelEvents(client_control); + ::CloseHandle(client_control->server_alive); + + delete[] reinterpret_cast(client_control); +} + +// This is the server thread that very slowly answers an IPC and exits. Note +// that the pong event needs to be signaled twice. +DWORD WINAPI SlowResponseServer(PVOID param) { + ServerEvents* events = reinterpret_cast(param); + DWORD wait_result = 0; + wait_result = ::WaitForSingleObject(events->ping, INFINITE); + ::Sleep(kIPCWaitTimeOut1 + kIPCWaitTimeOut2 + 200); + ::InterlockedExchange(events->state, kAckChannel); + ::SetEvent(events->pong); + return wait_result; +} + +// This thread's job is to keep the mutex locked. +DWORD WINAPI MainServerThread(PVOID param) { + ServerEvents* events = reinterpret_cast(param); + DWORD wait_result = 0; + wait_result = ::WaitForSingleObject(events->mutex, INFINITE); + Sleep(kIPCWaitTimeOut1 * 20); + return wait_result; +} + +// Creates a server thread that answers the IPC so slow that is guaranteed to +// trigger the time-out code path in the client. A second thread is created +// to hold locked the server_alive mutex: this signals the client that the +// server is not dead and it retries the wait. +TEST(IPCTest, ClientSlowServer) { + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096*2, &base_start); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY); + client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + ServerEvents events = {0}; + events.ping = client_control->channels[0].ping_event; + events.pong = client_control->channels[0].pong_event; + events.state = &client_control->channels[0].state; + + HANDLE t1 = ::CreateThread(NULL, 0, SlowResponseServer, &events, 0, NULL); + ASSERT_TRUE(NULL != t1); + ::CloseHandle(t1); + + ServerEvents events2 = {0}; + events2.pong = events.pong; + events2.mutex = client_control->server_alive; + + HANDLE t2 = ::CreateThread(NULL, 0, MainServerThread, &events2, 0, NULL); + ASSERT_TRUE(NULL != t2); + ::CloseHandle(t2); + + ::Sleep(1); + + void* buff0 = client.GetBuffer(); + uint32 tag = 4321; + CrossCallReturn answer; + CrossCallParamsMock* params1 = new(buff0) CrossCallParamsMock(tag, 1); + FakeOkAnswerInChannel(buff0); + + ResultCode result = client.DoCall(params1, &answer); + if (SBOX_ERROR_CHANNEL_ERROR != result) + client.FreeBuffer(buff0); + + EXPECT_TRUE(SBOX_ALL_OK == result); + EXPECT_EQ(tag, client_control->channels[0].ipc_tag); + EXPECT_EQ(kFreeChannel, client_control->channels[0].state); + + CloseChannelEvents(client_control); + ::CloseHandle(client_control->server_alive); + delete[] reinterpret_cast(client_control); +} + +// This test-only IPC dispatcher has two handlers with the same signature +// but only CallOneHandler should be used. +class UnitTestIPCDispatcher : public Dispatcher { + public: + enum { + CALL_ONE_TAG = 78, + CALL_TWO_TAG = 87 + }; + + UnitTestIPCDispatcher(); + ~UnitTestIPCDispatcher() {}; + + virtual bool SetupService(InterceptionManager* manager, int service) { + return true; + } + + private: + bool CallOneHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) { + ipc->return_info.extended[0].handle = p1; + ipc->return_info.extended[1].unsigned_int = p2; + return true; + } + + bool CallTwoHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) { + return true; + } +}; + +UnitTestIPCDispatcher::UnitTestIPCDispatcher() { + static const IPCCall call_one = { + {CALL_ONE_TAG, VOIDPTR_TYPE, ULONG_TYPE}, + reinterpret_cast( + &UnitTestIPCDispatcher::CallOneHandler) + }; + static const IPCCall call_two = { + {CALL_TWO_TAG, VOIDPTR_TYPE, ULONG_TYPE}, + reinterpret_cast( + &UnitTestIPCDispatcher::CallTwoHandler) + }; + ipc_calls_.push_back(call_one); + ipc_calls_.push_back(call_two); +} + +// This test does most of the shared memory IPC client-server roundtrip +// and tests the packing, unpacking and call dispatching. +TEST(IPCTest, SharedMemServerTests) { + size_t base_start = 0; + IPCControl* client_control = + MakeChannels(kIPCChannelSize, 4096, &base_start); + client_control->server_alive = HANDLE(1); + FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); + + char* mem = reinterpret_cast(client_control); + SharedMemIPCClient client(mem); + + CrossCallReturn answer; + HANDLE bar = HANDLE(191919); + DWORD foo = 6767676; + CrossCall(client, UnitTestIPCDispatcher::CALL_ONE_TAG, bar, foo, &answer); + void* buff = client.GetBuffer(); + ASSERT_TRUE(NULL != buff); + + UnitTestIPCDispatcher dispatcher; + // Since we are directly calling InvokeCallback, most of this structure + // can be set to NULL. + sandbox::SharedMemIPCServer::ServerControl srv_control = { + NULL, NULL, kIPCChannelSize, NULL, + reinterpret_cast(client_control), + NULL, &dispatcher, {0} }; + + sandbox::CrossCallReturn call_return = {0}; + EXPECT_TRUE(SharedMemIPCServer::InvokeCallback(&srv_control, buff, + &call_return)); + EXPECT_EQ(SBOX_ALL_OK, call_return.call_outcome); + EXPECT_TRUE(bar == call_return.extended[0].handle); + EXPECT_EQ(foo, call_return.extended[1].unsigned_int); + + CloseChannelEvents(client_control); + delete[] reinterpret_cast(client_control); +} + +} // namespace sandbox diff --git a/sandbox/win/src/job.cc b/sandbox/win/src/job.cc new file mode 100644 index 0000000..8ed3a77 --- /dev/null +++ b/sandbox/win/src/job.cc @@ -0,0 +1,116 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/job.h" + +#include "base/win/windows_version.h" +#include "sandbox/src/restricted_token.h" + +namespace sandbox { + +Job::~Job() { + if (job_handle_) + ::CloseHandle(job_handle_); +}; + +DWORD Job::Init(JobLevel security_level, wchar_t *job_name, + DWORD ui_exceptions) { + if (job_handle_) + return ERROR_ALREADY_INITIALIZED; + + job_handle_ = ::CreateJobObject(NULL, // No security attribute + job_name); + if (!job_handle_) + return ::GetLastError(); + + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; + JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; + + // Set the settings for the different security levels. Note: The higher levels + // inherit from the lower levels. + switch (security_level) { + case JOB_LOCKDOWN: { + jeli.BasicLimitInformation.LimitFlags |= + JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; + } + case JOB_RESTRICTED: { + jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD; + jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_READCLIPBOARD; + jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; + jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_GLOBALATOMS; + } + case JOB_LIMITED_USER: { + jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DISPLAYSETTINGS; + jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS; + jeli.BasicLimitInformation.ActiveProcessLimit = 1; + } + case JOB_INTERACTIVE: { + jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS; + jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DESKTOP; + jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS; + } + case JOB_UNPROTECTED: { + // The JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag is not supported on + // Windows 2000. We need a mechanism on Windows 2000 to ensure + // that processes in the job are terminated when the job is closed + if (base::win::GetVersion() == base::win::VERSION_PRE_XP) + break; + + jeli.BasicLimitInformation.LimitFlags |= + JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + break; + } + default: { + return ERROR_BAD_ARGUMENTS; + } + } + + if (FALSE == ::SetInformationJobObject(job_handle_, + JobObjectExtendedLimitInformation, + &jeli, + sizeof(jeli))) { + return ::GetLastError(); + } + + jbur.UIRestrictionsClass = jbur.UIRestrictionsClass & (~ui_exceptions); + if (FALSE == ::SetInformationJobObject(job_handle_, + JobObjectBasicUIRestrictions, + &jbur, + sizeof(jbur))) { + return ::GetLastError(); + } + + return ERROR_SUCCESS; +} + +DWORD Job::UserHandleGrantAccess(HANDLE handle) { + if (!job_handle_) + return ERROR_NO_DATA; + + if (!::UserHandleGrantAccess(handle, + job_handle_, + TRUE)) { // Access allowed. + return ::GetLastError(); + } + + return ERROR_SUCCESS; +} + +HANDLE Job::Detach() { + HANDLE handle_temp = job_handle_; + job_handle_ = NULL; + return handle_temp; +} + +DWORD Job::AssignProcessToJob(HANDLE process_handle) { + if (!job_handle_) + return ERROR_NO_DATA; + + if (FALSE == ::AssignProcessToJobObject(job_handle_, process_handle)) + return ::GetLastError(); + + return ERROR_SUCCESS; +} + +} // namespace sandbox diff --git a/sandbox/win/src/job.h b/sandbox/win/src/job.h new file mode 100644 index 0000000..a3f6738 --- /dev/null +++ b/sandbox/win/src/job.h @@ -0,0 +1,62 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_JOB_H_ +#define SANDBOX_SRC_JOB_H_ + +#include "base/basictypes.h" +#include "sandbox/src/restricted_token_utils.h" + +namespace sandbox { + +// Handles the creation of job objects based on a security profile. +// Sample usage: +// Job job; +// job.Init(JOB_LOCKDOWN, NULL); //no job name +// job.AssignProcessToJob(process_handle); +class Job { + public: + Job() : job_handle_(NULL) { } + + ~Job(); + + // Initializes and creates the job object. The security of the job is based + // on the security_level parameter. + // job_name can be NULL if the job is unnamed. + // If the chosen profile has too many ui restrictions, you can disable some + // by specifying them in the ui_exceptions parameters. + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + DWORD Init(JobLevel security_level, wchar_t *job_name, DWORD ui_exceptions); + + // Assigns the process referenced by process_handle to the job. + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + DWORD AssignProcessToJob(HANDLE process_handle); + + // Grants access to "handle" to the job. All processes in the job can + // subsequently recognize and use the handle. + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + DWORD UserHandleGrantAccess(HANDLE handle); + + // Revokes ownership to the job handle and returns it. The destructor of the + // class won't close the handle when called. + // If the object is not yet initialized, it returns 0. + HANDLE Detach(); + + private: + // Handle to the job referenced by the object. + HANDLE job_handle_; + + DISALLOW_COPY_AND_ASSIGN(Job); +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_JOB_H_ diff --git a/sandbox/win/src/job_unittest.cc b/sandbox/win/src/job_unittest.cc new file mode 100644 index 0000000..f386f1f --- /dev/null +++ b/sandbox/win/src/job_unittest.cc @@ -0,0 +1,189 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains unit tests for the job object. + +#include "base/win/scoped_process_information.h" +#include "sandbox/src/job.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +// Tests the creation and destruction of the job. +TEST(JobTest, TestCreation) { + // Scope the creation of Job. + { + // Create the job. + Job job; + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); + + // check if the job exists. + HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, + L"my_test_job_name"); + ASSERT_TRUE(job_handle != NULL); + + if (job_handle) + CloseHandle(job_handle); + } + + // Check if the job is destroyed when the object goes out of scope. + HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, L"my_test_job_name"); + ASSERT_TRUE(job_handle == NULL); + ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); +} + +// Tests the method "Detach". +TEST(JobTest, TestDetach) { + HANDLE job_handle; + // Scope the creation of Job. + { + // Create the job. + Job job; + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); + + job_handle = job.Detach(); + ASSERT_TRUE(job_handle != NULL); + } + + // Check to be sure that the job is still alive even after the object is gone + // out of scope. + HANDLE job_handle_dup = ::OpenJobObjectW(GENERIC_ALL, FALSE, + L"my_test_job_name"); + ASSERT_TRUE(job_handle_dup != NULL); + + // Remove all references. + if (job_handle_dup) + ::CloseHandle(job_handle_dup); + + if (job_handle) + ::CloseHandle(job_handle); + + // Check if the jbo is really dead. + job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, L"my_test_job_name"); + ASSERT_TRUE(job_handle == NULL); + ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); +} + +// Tests the ui exceptions +TEST(JobTest, TestExceptions) { + HANDLE job_handle; + // Scope the creation of Job. + { + // Create the job. + Job job; + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", + JOB_OBJECT_UILIMIT_READCLIPBOARD)); + + job_handle = job.Detach(); + ASSERT_TRUE(job_handle != NULL); + + JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; + DWORD size = sizeof(jbur); + BOOL result = ::QueryInformationJobObject(job_handle, + JobObjectBasicUIRestrictions, + &jbur, size, &size); + ASSERT_TRUE(result); + + ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD, 0); + ::CloseHandle(job_handle); + } + + // Scope the creation of Job. + { + // Create the job. + Job job; + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); + + job_handle = job.Detach(); + ASSERT_TRUE(job_handle != NULL); + + JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; + DWORD size = sizeof(jbur); + BOOL result = ::QueryInformationJobObject(job_handle, + JobObjectBasicUIRestrictions, + &jbur, size, &size); + ASSERT_TRUE(result); + + ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD, + JOB_OBJECT_UILIMIT_READCLIPBOARD); + ::CloseHandle(job_handle); + } +} + +// Tests the error case when the job is initialized twice. +TEST(JobTest, DoubleInit) { + // Create the job. + Job job; + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); + ASSERT_EQ(ERROR_ALREADY_INITIALIZED, job.Init(JOB_LOCKDOWN, L"test", 0)); +} + +// Tests the error case when we use a method and the object is not yet +// initialized. +TEST(JobTest, NoInit) { + Job job; + ASSERT_EQ(ERROR_NO_DATA, job.UserHandleGrantAccess(NULL)); + ASSERT_EQ(ERROR_NO_DATA, job.AssignProcessToJob(NULL)); + ASSERT_TRUE(job.Detach() == NULL); +} + +// Tests the initialization of the job with different security level. +TEST(JobTest, SecurityLevel) { + Job job1; + ASSERT_EQ(ERROR_SUCCESS, job1.Init(JOB_LOCKDOWN, L"job1", 0)); + + Job job2; + ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0)); + + Job job3; + ASSERT_EQ(ERROR_SUCCESS, job3.Init(JOB_LIMITED_USER, L"job3", 0)); + + Job job4; + ASSERT_EQ(ERROR_SUCCESS, job4.Init(JOB_INTERACTIVE, L"job4", 0)); + + Job job5; + ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 0)); + + Job job6; + ASSERT_EQ(ERROR_BAD_ARGUMENTS, job6.Init( + static_cast(JOB_UNPROTECTED+1), L"job6", 0)); +} + +// Tests the method "AssignProcessToJob". +TEST(JobTest, ProcessInJob) { + // Create the job. + Job job; + ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_UNPROTECTED, L"job_test_process", 0)); + + BOOL result = FALSE; + + wchar_t notepad[] = L"notepad"; + STARTUPINFO si = { sizeof(si) }; + base::win::ScopedProcessInformation pi; + result = ::CreateProcess(NULL, notepad, NULL, NULL, FALSE, 0, NULL, NULL, &si, + pi.Receive()); + ASSERT_TRUE(result); + ASSERT_EQ(ERROR_SUCCESS, job.AssignProcessToJob(pi.process_handle())); + + // Get the job handle. + HANDLE job_handle = job.Detach(); + + // Check if the process is in the job. + JOBOBJECT_BASIC_PROCESS_ID_LIST jbpidl = {0}; + DWORD size = sizeof(jbpidl); + result = ::QueryInformationJobObject(job_handle, + JobObjectBasicProcessIdList, + &jbpidl, size, &size); + EXPECT_TRUE(result); + + EXPECT_EQ(1, jbpidl.NumberOfAssignedProcesses); + EXPECT_EQ(1, jbpidl.NumberOfProcessIdsInList); + EXPECT_EQ(pi.process_id(), jbpidl.ProcessIdList[0]); + + EXPECT_TRUE(::TerminateProcess(pi.process_handle(), 0)); + + EXPECT_TRUE(::CloseHandle(job_handle)); +} + +} // namespace sandbox diff --git a/sandbox/win/src/named_pipe_dispatcher.cc b/sandbox/win/src/named_pipe_dispatcher.cc new file mode 100644 index 0000000..0569784 --- /dev/null +++ b/sandbox/win/src/named_pipe_dispatcher.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/named_pipe_dispatcher.h" + +#include "base/basictypes.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/named_pipe_interception.h" +#include "sandbox/src/named_pipe_policy.h" +#include "sandbox/src/policy_broker.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox.h" + + +namespace sandbox { + +NamedPipeDispatcher::NamedPipeDispatcher(PolicyBase* policy_base) + : policy_base_(policy_base) { + static const IPCCall create_params = { + {IPC_CREATENAMEDPIPEW_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE, + ULONG_TYPE, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast(&NamedPipeDispatcher::CreateNamedPipe) + }; + + ipc_calls_.push_back(create_params); +} + +bool NamedPipeDispatcher::SetupService(InterceptionManager* manager, + int service) { + if (IPC_CREATENAMEDPIPEW_TAG == service) + return INTERCEPT_EAT(manager, L"kernel32.dll", CreateNamedPipeW, + CREATE_NAMED_PIPE_ID, 36); + + return false; +} + +bool NamedPipeDispatcher::CreateNamedPipe( + IPCInfo* ipc, std::wstring* name, DWORD open_mode, DWORD pipe_mode, + DWORD max_instances, DWORD out_buffer_size, DWORD in_buffer_size, + DWORD default_timeout) { + const wchar_t* pipe_name = name->c_str(); + CountedParameterSet params; + params[NameBased::NAME] = ParamPickerMake(pipe_name); + + EvalResult eval = policy_base_->EvalPolicy(IPC_CREATENAMEDPIPEW_TAG, + params.GetBase()); + + HANDLE pipe; + DWORD ret = NamedPipePolicy::CreateNamedPipeAction(eval, *ipc->client_info, + *name, open_mode, + pipe_mode, max_instances, + out_buffer_size, + in_buffer_size, + default_timeout, &pipe); + + ipc->return_info.win32_result = ret; + ipc->return_info.handle = pipe; + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/named_pipe_dispatcher.h b/sandbox/win/src/named_pipe_dispatcher.h new file mode 100644 index 0000000..87a68b6 --- /dev/null +++ b/sandbox/win/src/named_pipe_dispatcher.h @@ -0,0 +1,37 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__ +#define SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__ + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sandbox_policy_base.h" + +namespace sandbox { + +// This class handles named pipe related IPC calls. +class NamedPipeDispatcher : public Dispatcher { + public: + explicit NamedPipeDispatcher(PolicyBase* policy_base); + ~NamedPipeDispatcher() {} + + // Dispatcher interface. + virtual bool SetupService(InterceptionManager* manager, int service); + + private: + // Processes IPC requests coming from calls to CreateNamedPipeW() in the + // target. + bool CreateNamedPipe(IPCInfo* ipc, std::wstring* name, DWORD open_mode, + DWORD pipe_mode, DWORD max_instances, + DWORD out_buffer_size, DWORD in_buffer_size, + DWORD default_timeout); + + PolicyBase* policy_base_; + DISALLOW_COPY_AND_ASSIGN(NamedPipeDispatcher); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__ diff --git a/sandbox/win/src/named_pipe_interception.cc b/sandbox/win/src/named_pipe_interception.cc new file mode 100644 index 0000000..4599441 --- /dev/null +++ b/sandbox/win/src/named_pipe_interception.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/named_pipe_interception.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/policy_target.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +HANDLE WINAPI TargetCreateNamedPipeW( + CreateNamedPipeWFunction orig_CreateNamedPipeW, LPCWSTR pipe_name, + DWORD open_mode, DWORD pipe_mode, DWORD max_instance, DWORD out_buffer_size, + DWORD in_buffer_size, DWORD default_timeout, + LPSECURITY_ATTRIBUTES security_attributes) { + HANDLE pipe = orig_CreateNamedPipeW(pipe_name, open_mode, pipe_mode, + max_instance, out_buffer_size, + in_buffer_size, default_timeout, + security_attributes); + if (INVALID_HANDLE_VALUE != pipe) + return pipe; + + DWORD original_error = ::GetLastError(); + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return INVALID_HANDLE_VALUE; + + // We don't support specific Security Attributes. + if (security_attributes) + return INVALID_HANDLE_VALUE; + + do { + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + CountedParameterSet params; + params[NameBased::NAME] = ParamPickerMake(pipe_name); + + if (!QueryBroker(IPC_CREATENAMEDPIPEW_TAG, params.GetBase())) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_CREATENAMEDPIPEW_TAG, pipe_name, + open_mode, pipe_mode, max_instance, + out_buffer_size, in_buffer_size, + default_timeout, &answer); + if (SBOX_ALL_OK != code) + break; + + ::SetLastError(answer.win32_result); + + if (ERROR_SUCCESS != answer.win32_result) + return INVALID_HANDLE_VALUE; + + return answer.handle; + } while (false); + + ::SetLastError(original_error); + return INVALID_HANDLE_VALUE; +} + +} // namespace sandbox diff --git a/sandbox/win/src/named_pipe_interception.h b/sandbox/win/src/named_pipe_interception.h new file mode 100644 index 0000000..5e2b334 --- /dev/null +++ b/sandbox/win/src/named_pipe_interception.h @@ -0,0 +1,36 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +#ifndef SANDBOX_SRC_NAMED_PIPE_INTERCEPTION_H__ +#define SANDBOX_SRC_NAMED_PIPE_INTERCEPTION_H__ + +namespace sandbox { + +extern "C" { + +typedef HANDLE (WINAPI *CreateNamedPipeWFunction) ( + LPCWSTR lpName, + DWORD dwOpenMode, + DWORD dwPipeMode, + DWORD nMaxInstances, + DWORD nOutBufferSize, + DWORD nInBufferSize, + DWORD nDefaultTimeOut, + LPSECURITY_ATTRIBUTES lpSecurityAttributes); + +// Interception of CreateNamedPipeW in kernel32.dll +SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateNamedPipeW( + CreateNamedPipeWFunction orig_CreateNamedPipeW, LPCWSTR pipe_name, + DWORD open_mode, DWORD pipe_mode, DWORD max_instance, DWORD out_buffer_size, + DWORD in_buffer_size, DWORD default_timeout, + LPSECURITY_ATTRIBUTES security_attributes); + +} // extern "C" + +} // namespace sandbox + +#endif // SANDBOX_SRC_NAMED_PIPE_INTERCEPTION_H__ diff --git a/sandbox/win/src/named_pipe_policy.cc b/sandbox/win/src/named_pipe_policy.cc new file mode 100644 index 0000000..00182cf --- /dev/null +++ b/sandbox/win/src/named_pipe_policy.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/named_pipe_policy.h" + +#include + +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_engine_opcodes.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox_types.h" + +namespace { + +// Creates a named pipe and duplicates the handle to 'target_process'. The +// remaining parameters are the same as CreateNamedPipeW(). +HANDLE CreateNamedPipeHelper(HANDLE target_process, LPCWSTR pipe_name, + DWORD open_mode, DWORD pipe_mode, + DWORD max_instances, DWORD out_buffer_size, + DWORD in_buffer_size, DWORD default_timeout, + LPSECURITY_ATTRIBUTES security_attributes) { + HANDLE pipe = ::CreateNamedPipeW(pipe_name, open_mode, pipe_mode, + max_instances, out_buffer_size, + in_buffer_size, default_timeout, + security_attributes); + if (INVALID_HANDLE_VALUE == pipe) + return pipe; + + HANDLE new_pipe; + if (!::DuplicateHandle(::GetCurrentProcess(), pipe, target_process, &new_pipe, + 0, FALSE, DUPLICATE_CLOSE_SOURCE | + DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(pipe); + return INVALID_HANDLE_VALUE; + } + + return new_pipe; +} + +} // namespace + +namespace sandbox { + +bool NamedPipePolicy::GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy) { + if (TargetPolicy::NAMEDPIPES_ALLOW_ANY != semantics) { + return false; + } + PolicyRule pipe(ASK_BROKER); + if (!pipe.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) { + return false; + } + if (!policy->AddRule(IPC_CREATENAMEDPIPEW_TAG, &pipe)) { + return false; + } + return true; +} + +DWORD NamedPipePolicy::CreateNamedPipeAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &name, + DWORD open_mode, DWORD pipe_mode, + DWORD max_instances, + DWORD out_buffer_size, + DWORD in_buffer_size, + DWORD default_timeout, + HANDLE* pipe) { + // The only action supported is ASK_BROKER which means create the pipe. + if (ASK_BROKER != eval_result) { + return ERROR_ACCESS_DENIED; + } + + *pipe = CreateNamedPipeHelper(client_info.process, name.c_str(), + open_mode, pipe_mode, max_instances, + out_buffer_size, in_buffer_size, + default_timeout, NULL); + + if (INVALID_HANDLE_VALUE == *pipe) + return ERROR_ACCESS_DENIED; + + return ERROR_SUCCESS; +} + +} // namespace sandbox diff --git a/sandbox/win/src/named_pipe_policy.h b/sandbox/win/src/named_pipe_policy.h new file mode 100644 index 0000000..2b6b09d --- /dev/null +++ b/sandbox/win/src/named_pipe_policy.h @@ -0,0 +1,45 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_NAMED_PIPE_POLICY_H__ +#define SANDBOX_SRC_NAMED_PIPE_POLICY_H__ + +#include + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/policy_low_level.h" +#include "sandbox/src/sandbox_policy.h" + +namespace sandbox { + +enum EvalResult; + +// This class centralizes most of the knowledge related to named pipe creation. +class NamedPipePolicy { + public: + // Creates the required low-level policy rules to evaluate a high-level. + // policy rule for named pipe creation + // 'name' is the named pipe to be created + // 'semantics' is the desired semantics. + // 'policy' is the policy generator to which the rules are going to be added. + static bool GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy); + + // Processes a 'CreateNamedPipeW()' request from the target. + static DWORD CreateNamedPipeAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &name, + DWORD open_mode, DWORD pipe_mode, + DWORD max_instances, + DWORD out_buffer_size, + DWORD in_buffer_size, + DWORD default_timeout, HANDLE* pipe); +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_NAMED_PIPE_POLICY_H__ diff --git a/sandbox/win/src/named_pipe_policy_test.cc b/sandbox/win/src/named_pipe_policy_test.cc new file mode 100644 index 0000000..11ddbc3 --- /dev/null +++ b/sandbox/win/src/named_pipe_policy_test.cc @@ -0,0 +1,78 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/tests/common/controller.h" + +namespace sandbox { + + +SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t **argv) { + if (argc != 1) { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + if ((NULL == argv) || (NULL == argv[0])) { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + + HANDLE pipe = ::CreateNamedPipeW(argv[0], + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 4096, + 4096, 2000, NULL); + if (INVALID_HANDLE_VALUE == pipe) + return SBOX_TEST_DENIED; + + OVERLAPPED overlapped = {0}; + overlapped.hEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL); + BOOL result = ::ConnectNamedPipe(pipe, &overlapped); + + if (!result) { + DWORD error = ::GetLastError(); + if (ERROR_PIPE_CONNECTED != error && + ERROR_IO_PENDING != error) { + return SBOX_TEST_FAILED; + } + } + + if (!::CloseHandle(pipe)) + return SBOX_TEST_FAILED; + + ::CloseHandle(overlapped.hEvent); + return SBOX_TEST_SUCCEEDED; +} + +// Tests if we can create a pipe in the sandbox. On XP, the sandbox can create +// a pipe without any help but it fails on Vista, this is why we do not test +// the "denied" case. +TEST(NamedPipePolicyTest, CreatePipe) { + TestRunner runner; + // TODO(nsylvain): This policy is wrong because "*" is a valid char in a + // namedpipe name. Here we apply it like a wildcard. http://b/893603 + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES, + TargetPolicy::NAMEDPIPES_ALLOW_ANY, + L"\\\\.\\pipe\\test*")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); +} + +// The same test as CreatePipe but this time using strict interceptions. +TEST(NamedPipePolicyTest, CreatePipeStrictInterceptions) { + TestRunner runner; + runner.GetPolicy()->SetStrictInterceptions(); + + // TODO(nsylvain): This policy is wrong because "*" is a valid char in a + // namedpipe name. Here we apply it like a wildcard. http://b/893603 + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES, + TargetPolicy::NAMEDPIPES_ALLOW_ANY, + L"\\\\.\\pipe\\test*")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); +} + +} // namespace sandbox diff --git a/sandbox/win/src/nt_internals.h b/sandbox/win/src/nt_internals.h new file mode 100644 index 0000000..fe4fcd6 --- /dev/null +++ b/sandbox/win/src/nt_internals.h @@ -0,0 +1,611 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file holds definitions related to the ntdll API. + +#ifndef SANDBOX_SRC_NT_INTERNALS_H__ +#define SANDBOX_SRC_NT_INTERNALS_H__ + +#include + +typedef LONG NTSTATUS; +#define NT_SUCCESS(st) (st >= 0) + +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) +#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) +#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L) +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +#ifndef STATUS_INVALID_PARAMETER +// It is now defined in Windows 2008 SDK. +#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL) +#endif +#define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018L) +#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) +#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) +#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L) +#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL) +#define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL) +#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL) + +#define CURRENT_PROCESS ((HANDLE) -1) +#define CURRENT_THREAD ((HANDLE) -2) +#define NtCurrentProcess CURRENT_PROCESS + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; +typedef UNICODE_STRING *PUNICODE_STRING; +typedef const UNICODE_STRING *PCUNICODE_STRING; + +typedef struct _STRING { + USHORT Length; + USHORT MaximumLength; + PCHAR Buffer; +} STRING; +typedef STRING *PSTRING; + +typedef STRING ANSI_STRING; +typedef PSTRING PANSI_STRING; +typedef CONST PSTRING PCANSI_STRING; + +typedef STRING OEM_STRING; +typedef PSTRING POEM_STRING; +typedef CONST STRING* PCOEM_STRING; + +#define OBJ_CASE_INSENSITIVE 0x00000040L + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES; +typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES; + +#define InitializeObjectAttributes(p, n, a, r, s) { \ + (p)->Length = sizeof(OBJECT_ATTRIBUTES);\ + (p)->RootDirectory = r;\ + (p)->Attributes = a;\ + (p)->ObjectName = n;\ + (p)->SecurityDescriptor = s;\ + (p)->SecurityQualityOfService = NULL;\ +} + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +// ----------------------------------------------------------------------- +// File IO + +// Create disposition values. + +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 +#define FILE_MAXIMUM_DISPOSITION 0x00000005 + +// Create/open option flags. + +#define FILE_DIRECTORY_FILE 0x00000001 +#define FILE_WRITE_THROUGH 0x00000002 +#define FILE_SEQUENTIAL_ONLY 0x00000004 +#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 + +#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 +#define FILE_NON_DIRECTORY_FILE 0x00000040 +#define FILE_CREATE_TREE_CONNECTION 0x00000080 + +#define FILE_COMPLETE_IF_OPLOCKED 0x00000100 +#define FILE_NO_EA_KNOWLEDGE 0x00000200 +#define FILE_OPEN_REMOTE_INSTANCE 0x00000400 +#define FILE_RANDOM_ACCESS 0x00000800 + +#define FILE_DELETE_ON_CLOSE 0x00001000 +#define FILE_OPEN_BY_FILE_ID 0x00002000 +#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 +#define FILE_NO_COMPRESSION 0x00008000 + +#define FILE_RESERVE_OPFILTER 0x00100000 +#define FILE_OPEN_REPARSE_POINT 0x00200000 +#define FILE_OPEN_NO_RECALL 0x00400000 +#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000 + +typedef NTSTATUS (WINAPI *NtCreateFileFunction)( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER AllocationSize OPTIONAL, + IN ULONG FileAttributes, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN PVOID EaBuffer OPTIONAL, + IN ULONG EaLength); + +typedef NTSTATUS (WINAPI *NtOpenFileFunction)( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG ShareAccess, + IN ULONG OpenOptions); + +typedef NTSTATUS (WINAPI *NtCloseFunction)( + IN HANDLE Handle); + +typedef enum _FILE_INFORMATION_CLASS { + FileRenameInformation = 10 +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +typedef struct _FILE_RENAME_INFORMATION { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; + +typedef NTSTATUS (WINAPI *NtSetInformationFileFunction)( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID FileInformation, + IN ULONG Length, + IN FILE_INFORMATION_CLASS FileInformationClass); + +typedef struct FILE_BASIC_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + ULONG FileAttributes; +} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; + +typedef NTSTATUS (WINAPI *NtQueryAttributesFileFunction)( + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PFILE_BASIC_INFORMATION FileAttributes); + +typedef struct _FILE_NETWORK_OPEN_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; +} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION; + +typedef NTSTATUS (WINAPI *NtQueryFullAttributesFileFunction)( + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PFILE_NETWORK_OPEN_INFORMATION FileAttributes); + +// ----------------------------------------------------------------------- +// Sections + +typedef NTSTATUS (WINAPI *NtCreateSectionFunction)( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN PLARGE_INTEGER MaximumSize OPTIONAL, + IN ULONG SectionPageProtection, + IN ULONG AllocationAttributes, + IN HANDLE FileHandle OPTIONAL); + +typedef ULONG SECTION_INHERIT; +#define ViewShare 1 +#define ViewUnmap 2 + +typedef NTSTATUS (WINAPI *NtMapViewOfSectionFunction)( + IN HANDLE SectionHandle, + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN ULONG_PTR ZeroBits, + IN SIZE_T CommitSize, + IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, + IN OUT PSIZE_T ViewSize, + IN SECTION_INHERIT InheritDisposition, + IN ULONG AllocationType, + IN ULONG Win32Protect); + +typedef NTSTATUS (WINAPI *NtUnmapViewOfSectionFunction)( + IN HANDLE ProcessHandle, + IN PVOID BaseAddress); + +typedef enum _SECTION_INFORMATION_CLASS { + SectionBasicInformation = 0, + SectionImageInformation +} SECTION_INFORMATION_CLASS; + +typedef struct _SECTION_BASIC_INFORMATION { + PVOID BaseAddress; + ULONG Attributes; + LARGE_INTEGER Size; +} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; + +typedef NTSTATUS (WINAPI *NtQuerySectionFunction)( + IN HANDLE SectionHandle, + IN SECTION_INFORMATION_CLASS SectionInformationClass, + OUT PVOID SectionInformation, + IN SIZE_T SectionInformationLength, + OUT PSIZE_T ReturnLength OPTIONAL); + +// ----------------------------------------------------------------------- +// Process and Thread + +typedef struct _CLIENT_ID { + PVOID UniqueProcess; + PVOID UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef NTSTATUS (WINAPI *NtOpenThreadFunction) ( + OUT PHANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PCLIENT_ID ClientId); + +typedef NTSTATUS (WINAPI *NtOpenProcessFunction) ( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PCLIENT_ID ClientId); + +typedef enum _NT_THREAD_INFORMATION_CLASS { + ThreadBasicInformation, + ThreadTimes, + ThreadPriority, + ThreadBasePriority, + ThreadAffinityMask, + ThreadImpersonationToken, + ThreadDescriptorTableEntry, + ThreadEnableAlignmentFaultFixup, + ThreadEventPair, + ThreadQuerySetWin32StartAddress, + ThreadZeroTlsCell, + ThreadPerformanceCount, + ThreadAmILastThread, + ThreadIdealProcessor, + ThreadPriorityBoost, + ThreadSetTlsArrayAddress, + ThreadIsIoPending, + ThreadHideFromDebugger +} NT_THREAD_INFORMATION_CLASS, *PNT_THREAD_INFORMATION_CLASS; + +typedef NTSTATUS (WINAPI *NtSetInformationThreadFunction) ( + IN HANDLE ThreadHandle, + IN NT_THREAD_INFORMATION_CLASS ThreadInformationClass, + IN PVOID ThreadInformation, + IN ULONG ThreadInformationLength); + +// Partial definition only: +typedef enum _PROCESSINFOCLASS { + ProcessBasicInformation = 0 +} PROCESSINFOCLASS; + +typedef PVOID PPEB; +typedef PVOID KPRIORITY; + +typedef struct _PROCESS_BASIC_INFORMATION { + NTSTATUS ExitStatus; + PPEB PebBaseAddress; + KAFFINITY AffinityMask; + KPRIORITY BasePriority; + ULONG UniqueProcessId; + ULONG InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; + +typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)( + IN HANDLE ProcessHandle, + IN PROCESSINFOCLASS ProcessInformationClass, + OUT PVOID ProcessInformation, + IN ULONG ProcessInformationLength, + OUT PULONG ReturnLength OPTIONAL); + +typedef NTSTATUS (WINAPI *NtOpenThreadTokenFunction) ( + IN HANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN OpenAsSelf, + OUT PHANDLE TokenHandle); + +typedef NTSTATUS (WINAPI *NtOpenThreadTokenExFunction) ( + IN HANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN OpenAsSelf, + IN ULONG HandleAttributes, + OUT PHANDLE TokenHandle); + +typedef NTSTATUS (WINAPI *NtOpenProcessTokenFunction) ( + IN HANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + OUT PHANDLE TokenHandle); + +typedef NTSTATUS (WINAPI *NtOpenProcessTokenExFunction) ( + IN HANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG HandleAttributes, + OUT PHANDLE TokenHandle); + +typedef NTSTATUS (WINAPI * RtlCreateUserThreadFunction)( + IN HANDLE Process, + IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, + IN BOOLEAN CreateSuspended, + IN ULONG ZeroBits, + IN SIZE_T MaximumStackSize, + IN SIZE_T CommittedStackSize, + IN LPTHREAD_START_ROUTINE StartAddress, + IN PVOID Parameter, + OUT PHANDLE Thread, + OUT PCLIENT_ID ClientId); + +// ----------------------------------------------------------------------- +// Registry + +typedef NTSTATUS (WINAPI *NtCreateKeyFunction)( + OUT PHANDLE KeyHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG TitleIndex, + IN PUNICODE_STRING Class OPTIONAL, + IN ULONG CreateOptions, + OUT PULONG Disposition OPTIONAL); + +typedef NTSTATUS (WINAPI *NtOpenKeyFunction)( + OUT PHANDLE KeyHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI *NtOpenKeyExFunction)( + OUT PHANDLE KeyHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN DWORD open_options); + +typedef NTSTATUS (WINAPI *NtDeleteKeyFunction)( + IN HANDLE KeyHandle); + +// ----------------------------------------------------------------------- +// Memory + +// Don't really need this structure right now. +typedef PVOID PRTL_HEAP_PARAMETERS; + +typedef PVOID (WINAPI *RtlCreateHeapFunction)( + IN ULONG Flags, + IN PVOID HeapBase OPTIONAL, + IN SIZE_T ReserveSize OPTIONAL, + IN SIZE_T CommitSize OPTIONAL, + IN PVOID Lock OPTIONAL, + IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL); + +typedef PVOID (WINAPI *RtlDestroyHeapFunction)( + IN PVOID HeapHandle); + +typedef PVOID (WINAPI *RtlAllocateHeapFunction)( + IN PVOID HeapHandle, + IN ULONG Flags, + IN SIZE_T Size); + +typedef BOOLEAN (WINAPI *RtlFreeHeapFunction)( + IN PVOID HeapHandle, + IN ULONG Flags, + IN PVOID HeapBase); + +typedef NTSTATUS (WINAPI *NtAllocateVirtualMemoryFunction) ( + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN ULONG_PTR ZeroBits, + IN OUT PSIZE_T RegionSize, + IN ULONG AllocationType, + IN ULONG Protect); + +typedef NTSTATUS (WINAPI *NtFreeVirtualMemoryFunction) ( + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T RegionSize, + IN ULONG FreeType); + +typedef enum _MEMORY_INFORMATION_CLASS { + MemoryBasicInformation = 0, + MemoryWorkingSetList, + MemorySectionName, + MemoryBasicVlmInformation +} MEMORY_INFORMATION_CLASS; + +typedef struct _MEMORY_SECTION_NAME { // Information Class 2 + UNICODE_STRING SectionFileName; +} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME; + +typedef NTSTATUS (WINAPI *NtQueryVirtualMemoryFunction)( + IN HANDLE ProcessHandle, + IN PVOID BaseAddress, + IN MEMORY_INFORMATION_CLASS MemoryInformationClass, + OUT PVOID MemoryInformation, + IN ULONG MemoryInformationLength, + OUT PULONG ReturnLength OPTIONAL); + +typedef NTSTATUS (WINAPI *NtProtectVirtualMemoryFunction)( + IN HANDLE ProcessHandle, + IN OUT PVOID* BaseAddress, + IN OUT PSIZE_T ProtectSize, + IN ULONG NewProtect, + OUT PULONG OldProtect); + +// ----------------------------------------------------------------------- +// Objects + +typedef enum _OBJECT_INFORMATION_CLASS { + ObjectBasicInformation, + ObjectNameInformation, + ObjectTypeInformation, + ObjectAllInformation, + ObjectDataInformation +} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS; + +typedef struct _OBJDIR_INFORMATION { + UNICODE_STRING ObjectName; + UNICODE_STRING ObjectTypeName; + BYTE Data[1]; +} OBJDIR_INFORMATION; + +typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION { + ULONG Attributes; + ACCESS_MASK GrantedAccess; + ULONG HandleCount; + ULONG PointerCount; + ULONG Reserved[10]; // reserved for internal use +} PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION; + +typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION { + UNICODE_STRING TypeName; + ULONG Reserved[22]; // reserved for internal use +} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION; + +typedef enum _POOL_TYPE { + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + ReservedType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS +} POOL_TYPE; + +typedef struct _OBJECT_BASIC_INFORMATION { + ULONG Attributes; + ACCESS_MASK GrantedAccess; + ULONG HandleCount; + ULONG PointerCount; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; + ULONG Reserved[3]; + ULONG NameInformationLength; + ULONG TypeInformationLength; + ULONG SecurityDescriptorLength; + LARGE_INTEGER CreateTime; +} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION; + +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING Name; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccess; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + USHORT MaintainTypeList; + POOL_TYPE PoolType; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemHandleInformation = 16 +} SYSTEM_INFORMATION_CLASS; + +typedef struct _SYSTEM_HANDLE_INFORMATION { + USHORT ProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeNumber; + UCHAR Flags; + USHORT Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX { + ULONG NumberOfHandles; + SYSTEM_HANDLE_INFORMATION Information[1]; +} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; + +typedef struct _OBJECT_NAME_INFORMATION { + UNICODE_STRING ObjectName; +} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; + +typedef NTSTATUS (WINAPI *NtQueryObjectFunction)( + IN HANDLE Handle, + IN OBJECT_INFORMATION_CLASS ObjectInformationClass, + OUT PVOID ObjectInformation OPTIONAL, + IN ULONG ObjectInformationLength, + OUT PULONG ReturnLength OPTIONAL); + +typedef NTSTATUS (WINAPI *NtDuplicateObjectFunction)( + IN HANDLE SourceProcess, + IN HANDLE SourceHandle, + IN HANDLE TargetProcess, + OUT PHANDLE TargetHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG Attributes, + IN ULONG Options); + +typedef NTSTATUS (WINAPI *NtSignalAndWaitForSingleObjectFunction)( + IN HANDLE HandleToSignal, + IN HANDLE HandleToWait, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout OPTIONAL); + +typedef NTSTATUS (WINAPI *NtQuerySystemInformation)( + IN SYSTEM_INFORMATION_CLASS SystemInformationClass, + OUT PVOID SystemInformation, + IN ULONG SystemInformationLength, + OUT PULONG ReturnLength); + +typedef NTSTATUS (WINAPI *NtQueryObject)( + IN HANDLE Handle, + IN OBJECT_INFORMATION_CLASS ObjectInformationClass, + OUT PVOID ObjectInformation, + IN ULONG ObjectInformationLength, + OUT PULONG ReturnLength); + +// ----------------------------------------------------------------------- +// Strings + +typedef int (__cdecl *_strnicmpFunction)( + IN const char* _Str1, + IN const char* _Str2, + IN size_t _MaxCount); + +typedef size_t (__cdecl *strlenFunction)( + IN const char * _Str); + +typedef size_t (__cdecl *wcslenFunction)( + IN const wchar_t* _Str); + +typedef NTSTATUS (WINAPI *RtlAnsiStringToUnicodeStringFunction)( + IN OUT PUNICODE_STRING DestinationString, + IN PANSI_STRING SourceString, + IN BOOLEAN AllocateDestinationString); + +typedef LONG (WINAPI *RtlCompareUnicodeStringFunction)( + IN PCUNICODE_STRING String1, + IN PCUNICODE_STRING String2, + IN BOOLEAN CaseInSensitive); + +typedef VOID (WINAPI *RtlInitUnicodeStringFunction) ( + IN OUT PUNICODE_STRING DestinationString, + IN PCWSTR SourceString); + +#endif // SANDBOX_SRC_NT_INTERNALS_H__ diff --git a/sandbox/win/src/policy_broker.cc b/sandbox/win/src/policy_broker.cc new file mode 100644 index 0000000..61c64c6 --- /dev/null +++ b/sandbox/win/src/policy_broker.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "sandbox/src/policy_broker.h" + +#include "base/logging.h" +#include "base/win/pe_image.h" +#include "base/win/windows_version.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/policy_target.h" +#include "sandbox/src/process_thread_interception.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_nt_types.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/sandbox_utils.h" +#include "sandbox/src/target_process.h" + +// This code executes on the broker side, as a callback from the policy on the +// target side (the child). + +namespace sandbox { + +// This is the list of all imported symbols from ntdll.dll. +SANDBOX_INTERCEPT NtExports g_nt; + +#define INIT_GLOBAL_NT(member) \ + g_nt.member = reinterpret_cast( \ + ntdll_image.GetProcAddress("Nt" #member)); \ + if (NULL == g_nt.member) \ + return false + +#define INIT_GLOBAL_RTL(member) \ + g_nt.member = reinterpret_cast( \ + ntdll_image.GetProcAddress(#member)); \ + if (NULL == g_nt.member) \ + return false + +bool SetupNtdllImports(TargetProcess *child) { + HMODULE ntdll = ::GetModuleHandle(kNtdllName); + base::win::PEImage ntdll_image(ntdll); + + // Bypass purify's interception. + wchar_t* loader_get = reinterpret_cast( + ntdll_image.GetProcAddress("LdrGetDllHandle")); + if (loader_get) { + GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + loader_get, &ntdll); + } + + INIT_GLOBAL_NT(AllocateVirtualMemory); + INIT_GLOBAL_NT(Close); + INIT_GLOBAL_NT(DuplicateObject); + INIT_GLOBAL_NT(FreeVirtualMemory); + INIT_GLOBAL_NT(MapViewOfSection); + INIT_GLOBAL_NT(ProtectVirtualMemory); + INIT_GLOBAL_NT(QueryInformationProcess); + INIT_GLOBAL_NT(QueryObject); + INIT_GLOBAL_NT(QuerySection); + INIT_GLOBAL_NT(QueryVirtualMemory); + INIT_GLOBAL_NT(UnmapViewOfSection); + + INIT_GLOBAL_RTL(RtlAllocateHeap); + INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString); + INIT_GLOBAL_RTL(RtlCompareUnicodeString); + INIT_GLOBAL_RTL(RtlCreateHeap); + INIT_GLOBAL_RTL(RtlCreateUserThread); + INIT_GLOBAL_RTL(RtlDestroyHeap); + INIT_GLOBAL_RTL(RtlFreeHeap); + INIT_GLOBAL_RTL(_strnicmp); + INIT_GLOBAL_RTL(strlen); + INIT_GLOBAL_RTL(wcslen); + +#ifndef NDEBUG + // Verify that the structure is fully initialized. + for (size_t i = 0; i < sizeof(g_nt)/sizeof(void*); i++) + DCHECK(reinterpret_cast(&g_nt)[i]); +#endif + ResultCode ret = child->TransferVariable("g_nt", &g_nt, sizeof(g_nt)); + + return SBOX_ALL_OK == ret ? true : false; +} + +#undef INIT_GLOBAL_NT +#undef INIT_GLOBAL_RTL + +bool SetupBasicInterceptions(InterceptionManager* manager) { + // Interceptions provided by process_thread_policy, without actual policy. + if (!INTERCEPT_NT(manager, NtOpenThread, OPEN_TREAD_ID, 20) || + !INTERCEPT_NT(manager, NtOpenProcess, OPEN_PROCESS_ID, 20) || + !INTERCEPT_NT(manager, NtOpenProcessToken, OPEN_PROCESS_TOKEN_ID, 16)) + return false; + + // Interceptions with neither policy nor IPC. + if (!INTERCEPT_NT(manager, NtSetInformationThread, SET_INFORMATION_THREAD_ID, + 20) || + !INTERCEPT_NT(manager, NtOpenThreadToken, OPEN_THREAD_TOKEN_ID, 20)) + return false; + + if (base::win::GetVersion() >= base::win::VERSION_XP) { + // Bug 27218: We don't have dispatch for some x64 syscalls. + // This one is also provided by process_thread_policy. + if (!INTERCEPT_NT(manager, NtOpenProcessTokenEx, OPEN_PROCESS_TOKEN_EX_ID, + 20)) + return false; + + return INTERCEPT_NT(manager, NtOpenThreadTokenEx, OPEN_THREAD_TOKEN_EX_ID, + 24); + } + + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/policy_broker.h b/sandbox/win/src/policy_broker.h new file mode 100644 index 0000000..fd2602a --- /dev/null +++ b/sandbox/win/src/policy_broker.h @@ -0,0 +1,23 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_POLICY_BROKER_H_ +#define SANDBOX_SRC_POLICY_BROKER_H_ + +#include "sandbox/src/interception.h" + +namespace sandbox { + +class TargetProcess; + +// Sets up interceptions not controlled by explicit policies. +bool SetupBasicInterceptions(InterceptionManager* manager); + +// Sets up imports from NTDLL for the given target process so the interceptions +// can work. +bool SetupNtdllImports(TargetProcess *child); + +} // namespace sandbox + +#endif // SANDBOX_SRC_POLICY_BROKER_H_ diff --git a/sandbox/win/src/policy_engine_opcodes.cc b/sandbox/win/src/policy_engine_opcodes.cc new file mode 100644 index 0000000..8d9ceef --- /dev/null +++ b/sandbox/win/src/policy_engine_opcodes.cc @@ -0,0 +1,454 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/policy_engine_opcodes.h" + +#include "base/basictypes.h" +#include "sandbox/src/sandbox_nt_types.h" +#include "sandbox/src/sandbox_types.h" + +namespace { +const unsigned short kMaxUniStrSize = 0xfffc; + +bool InitStringUnicode(const wchar_t* source, size_t length, + UNICODE_STRING* ustring) { + ustring->Buffer = const_cast(source); + ustring->Length = static_cast(length) * sizeof(wchar_t); + if (length > kMaxUniStrSize) { + return false; + } + ustring->MaximumLength = (NULL != source) ? + ustring->Length + sizeof(wchar_t) : 0; + return true; +} + +} // namespace + +namespace sandbox { + +SANDBOX_INTERCEPT NtExports g_nt; + +// Note: The opcodes are implemented as functions (as opposed to classes derived +// from PolicyOpcode) because you should not add more member variables to the +// PolicyOpcode class since it would cause object slicing on the target. So to +// enforce that (instead of just trusting the developer) the opcodes became +// just functions. +// +// In the code that follows I have keep the evaluation function and the factory +// function together to stress the close relationship between both. For example, +// only the factory method and the evaluation function know the stored argument +// order and meaning. + +template +EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp, + MatchContext* match); + +////////////////////////////////////////////////////////////////////////////// +// Opcode OpAlwaysFalse: +// Does not require input parameter. + +PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) { + return MakeBase(OP_ALWAYS_FALSE, options, -1); +} + +template <> +EvalResult OpcodeEval(PolicyOpcode* opcode, + const ParameterSet* param, + MatchContext* context) { + UNREFERENCED_PARAMETER(opcode); + UNREFERENCED_PARAMETER(param); + UNREFERENCED_PARAMETER(context); + return EVAL_FALSE; +} + +////////////////////////////////////////////////////////////////////////////// +// Opcode OpAlwaysTrue: +// Does not require input parameter. + +PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) { + return MakeBase(OP_ALWAYS_TRUE, options, -1); +} + +template <> +EvalResult OpcodeEval(PolicyOpcode* opcode, + const ParameterSet* param, + MatchContext* context) { + UNREFERENCED_PARAMETER(opcode); + UNREFERENCED_PARAMETER(param); + UNREFERENCED_PARAMETER(context); + return EVAL_TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// Opcode OpAction: +// Does not require input parameter. +// Argument 0 contains the actual action to return. + +PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action, + uint32 options) { + PolicyOpcode* opcode = MakeBase(OP_ACTION, options, 0); + if (NULL == opcode) return NULL; + opcode->SetArgument(0, action); + return opcode; +} + +template <> +EvalResult OpcodeEval(PolicyOpcode* opcode, + const ParameterSet* param, + MatchContext* context) { + UNREFERENCED_PARAMETER(param); + UNREFERENCED_PARAMETER(context); + int action = 0; + opcode->GetArgument(0, &action); + return static_cast(action); +} + +////////////////////////////////////////////////////////////////////////////// +// Opcode OpNumberMatch: +// Requires a unsigned long or void* in selected_param +// Argument 0 is the stored number to match. +// Argument 1 is the C++ type of the 0th argument. + +PolicyOpcode* OpcodeFactory::MakeOpNumberMatch(int16 selected_param, + unsigned long match, + uint32 options) { + PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); + if (NULL == opcode) return NULL; + opcode->SetArgument(0, match); + opcode->SetArgument(1, ULONG_TYPE); + return opcode; +} + +PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16 selected_param, + const void* match, + uint32 options) { + PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); + if (NULL == opcode) return NULL; + opcode->SetArgument(0, match); + opcode->SetArgument(1, VOIDPTR_TYPE); + return opcode; +} + +template <> +EvalResult OpcodeEval(PolicyOpcode* opcode, + const ParameterSet* param, + MatchContext* context) { + UNREFERENCED_PARAMETER(context); + unsigned long value_ulong = 0; + if (param->Get(&value_ulong)) { + unsigned long match_ulong = 0; + opcode->GetArgument(0, &match_ulong); + return (match_ulong != value_ulong)? EVAL_FALSE : EVAL_TRUE; + } else { + const void* value_ptr = NULL; + if (param->Get(&value_ptr)) { + const void* match_ptr = NULL; + opcode->GetArgument(0, &match_ptr); + return (match_ptr != value_ptr)? EVAL_FALSE : EVAL_TRUE; + } + } + return EVAL_ERROR; +} + +////////////////////////////////////////////////////////////////////////////// +// Opcode OpUlongMatchRange +// Requires a unsigned long in selected_param. +// Argument 0 is the stored lower bound to match. +// Argument 1 is the stored upper bound to match. + +PolicyOpcode* OpcodeFactory::MakeOpUlongMatchRange(int16 selected_param, + unsigned long lower_bound, + unsigned long upper_bound, + uint32 options) { + if (lower_bound > upper_bound) { + return false; + } + PolicyOpcode* opcode = MakeBase(OP_ULONG_MATCH_RANGE, options, + selected_param); + if (NULL == opcode) return NULL; + opcode->SetArgument(0, lower_bound); + opcode->SetArgument(1, upper_bound); + return opcode; +} + +template <> +EvalResult OpcodeEval(PolicyOpcode* opcode, + const ParameterSet* param, + MatchContext* context) { + UNREFERENCED_PARAMETER(context); + unsigned long value = 0; + if (!param->Get(&value)) return EVAL_ERROR; + + unsigned long lower_bound = 0; + unsigned long upper_bound = 0; + opcode->GetArgument(0, &lower_bound); + opcode->GetArgument(1, &upper_bound); + return((lower_bound <= value) && (upper_bound >= value))? + EVAL_TRUE : EVAL_FALSE; +} + +////////////////////////////////////////////////////////////////////////////// +// Opcode OpUlongAndMatch: +// Requires a unsigned long in selected_param. +// Argument 0 is the stored number to match. + +PolicyOpcode* OpcodeFactory::MakeOpUlongAndMatch(int16 selected_param, + unsigned long match, + uint32 options) { + PolicyOpcode* opcode = MakeBase(OP_ULONG_AND_MATCH, options, selected_param); + if (NULL == opcode) return NULL; + opcode->SetArgument(0, match); + return opcode; +} + +template <> +EvalResult OpcodeEval(PolicyOpcode* opcode, + const ParameterSet* param, + MatchContext* context) { + UNREFERENCED_PARAMETER(context); + unsigned long value = 0; + if (!param->Get(&value)) return EVAL_ERROR; + + unsigned long number = 0; + opcode->GetArgument(0, &number); + return (number & value)? EVAL_TRUE : EVAL_FALSE; +} + +////////////////////////////////////////////////////////////////////////////// +// Opcode OpWStringMatch: +// Requires a wchar_t* in selected_param. +// Argument 0 is the byte displacement of the stored string. +// Argument 1 is the lenght in chars of the stored string. +// Argument 2 is the offset to apply on the input string. It has special values. +// as noted in the header file. +// Argument 3 is the string matching options. + +PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16 selected_param, + const wchar_t* match_str, + int start_position, + StringMatchOptions match_opts, + uint32 options) { + if (NULL == match_str) { + return NULL; + } + if ('\0' == match_str[0]) { + return NULL; + } + + int lenght = lstrlenW(match_str); + + PolicyOpcode* opcode = MakeBase(OP_WSTRING_MATCH, options, selected_param); + if (NULL == opcode) { + return NULL; + } + ptrdiff_t delta_str = AllocRelative(opcode, match_str, wcslen(match_str)+1); + if (0 == delta_str) { + return NULL; + } + opcode->SetArgument(0, delta_str); + opcode->SetArgument(1, lenght); + opcode->SetArgument(2, start_position); + opcode->SetArgument(3, match_opts); + return opcode; +} + +template<> +EvalResult OpcodeEval(PolicyOpcode* opcode, + const ParameterSet* param, + MatchContext* context) { + if (NULL == context) { + return EVAL_ERROR; + } + const wchar_t* source_str = NULL; + if (!param->Get(&source_str)) return EVAL_ERROR; + + int start_position = 0; + int match_len = 0; + unsigned int match_opts = 0; + opcode->GetArgument(1, &match_len); + opcode->GetArgument(2, &start_position); + opcode->GetArgument(3, &match_opts); + + const wchar_t* match_str = opcode->GetRelativeString(0); + // Advance the source string to the last successfully evaluated position + // according to the match context. + source_str = &source_str[context->position]; + int source_len = static_cast(g_nt.wcslen(source_str)); + + if (0 == source_len) { + // If we reached the end of the source string there is nothing we can + // match against. + return EVAL_FALSE; + } + if (match_len > source_len) { + // There can't be a positive match when the target string is bigger than + // the source string + return EVAL_FALSE; + } + + BOOL case_sensitive = (match_opts & CASE_INSENSITIVE) ? TRUE : FALSE; + + // We have three cases, depending on the value of start_pos: + // Case 1. We skip N characters and compare once. + // Case 2: We skip to the end and compare once. + // Case 3: We match the first substring (if we find any). + if (start_position >= 0) { + if (kSeekToEnd == start_position) { + start_position = source_len - match_len; + } else if (match_opts & EXACT_LENGHT) { + // A sub-case of case 3 is when the EXACT_LENGHT flag is on + // the match needs to be not just substring but full match. + if ((match_len + start_position) != source_len) { + return EVAL_FALSE; + } + } + + // Advance start_pos characters. Warning! this does not consider + // utf16 encodings (surrogate pairs) or other Unicode 'features'. + source_str += start_position; + + // Since we skipped, lets reevaluate just the lengths again. + if ((match_len + start_position) > source_len) { + return EVAL_FALSE; + } + + UNICODE_STRING match_ustr; + InitStringUnicode(match_str, match_len, &match_ustr); + UNICODE_STRING source_ustr; + InitStringUnicode(source_str, match_len, &source_ustr); + + if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, + case_sensitive)) { + // Match! update the match context. + context->position += start_position + match_len; + return EVAL_TRUE; + } else { + return EVAL_FALSE; + } + } else if (start_position < 0) { + UNICODE_STRING match_ustr; + InitStringUnicode(match_str, match_len, &match_ustr); + UNICODE_STRING source_ustr; + InitStringUnicode(source_str, match_len, &source_ustr); + + do { + if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, + case_sensitive)) { + // Match! update the match context. + context->position += (source_ustr.Buffer - source_str) + match_len; + return EVAL_TRUE; + } + ++source_ustr.Buffer; + --source_len; + } while (source_len >= match_len); + } + return EVAL_FALSE; +} + +////////////////////////////////////////////////////////////////////////////// +// OpcodeMaker (other member functions). + +PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id, + uint32 options, + int16 selected_param) { + if (memory_size() < sizeof(PolicyOpcode)) { + return NULL; + } + + // Create opcode using placement-new on the buffer memory. + PolicyOpcode* opcode = new(memory_top_) PolicyOpcode(); + + // Fill in the standard fields, that every opcode has. + memory_top_ += sizeof(PolicyOpcode); + opcode->opcode_id_ = opcode_id; + opcode->options_ = static_cast(options); + opcode->parameter_ = selected_param; + return opcode; +} + +ptrdiff_t OpcodeFactory::AllocRelative(void* start, const wchar_t* str, + size_t lenght) { + size_t bytes = lenght * sizeof(wchar_t); + if (memory_size() < bytes) { + return 0; + } + memory_bottom_ -= bytes; + if (reinterpret_cast(memory_bottom_) & 1) { + // TODO(cpu) replace this for something better. + ::DebugBreak(); + } + memcpy(memory_bottom_, str, bytes); + ptrdiff_t delta = memory_bottom_ - reinterpret_cast(start); + return delta; +} + +////////////////////////////////////////////////////////////////////////////// +// Opcode evaluation dispatchers. + +// This function is the one and only entry for evaluating any opcode. It is +// in charge of applying any relevant opcode options and calling EvaluateInner +// were the actual dispatch-by-id is made. It would seem at first glance that +// the dispatch should be done by virtual function (vtable) calls but you have +// to remember that the opcodes are made in the broker process and copied as +// raw memory to the target process. + +EvalResult PolicyOpcode::Evaluate(const ParameterSet* call_params, + size_t param_count, MatchContext* match) { + if (NULL == call_params) { + return EVAL_ERROR; + } + const ParameterSet* selected_param = NULL; + if (parameter_ >= 0) { + if (static_cast(parameter_) >= param_count) { + return EVAL_ERROR; + } + selected_param = &call_params[parameter_]; + } + EvalResult result = EvaluateHelper(selected_param, match); + + // Apply the general options regardless of the particular type of opcode. + if (kPolNone == options_) { + return result; + } + + if (options_ & kPolNegateEval) { + if (EVAL_TRUE == result) { + result = EVAL_FALSE; + } else if (EVAL_FALSE == result) { + result = EVAL_TRUE; + } else if (EVAL_ERROR != result) { + result = EVAL_ERROR; + } + } + if (NULL != match) { + if (options_ & kPolClearContext) { + match->Clear(); + } + if (options_ & kPolUseOREval) { + match->options = kPolUseOREval; + } + } + return result; +} + +#define OPCODE_EVAL(op, x, y, z) case op: return OpcodeEval(x, y, z) + +EvalResult PolicyOpcode::EvaluateHelper(const ParameterSet* parameters, + MatchContext* match) { + switch (opcode_id_) { + OPCODE_EVAL(OP_ALWAYS_FALSE, this, parameters, match); + OPCODE_EVAL(OP_ALWAYS_TRUE, this, parameters, match); + OPCODE_EVAL(OP_NUMBER_MATCH, this, parameters, match); + OPCODE_EVAL(OP_ULONG_MATCH_RANGE, this, parameters, match); + OPCODE_EVAL(OP_WSTRING_MATCH, this, parameters, match); + OPCODE_EVAL(OP_ULONG_AND_MATCH, this, parameters, match); + OPCODE_EVAL(OP_ACTION, this, parameters, match); + default: + return EVAL_ERROR; + } +} + +#undef OPCODE_EVAL + +} // namespace sandbox diff --git a/sandbox/win/src/policy_engine_opcodes.h b/sandbox/win/src/policy_engine_opcodes.h new file mode 100644 index 0000000..6821413 --- /dev/null +++ b/sandbox/win/src/policy_engine_opcodes.h @@ -0,0 +1,380 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_POLICY_ENGINE_OPCODES_H__ +#define SANDBOX_SRC_POLICY_ENGINE_OPCODES_H__ + +#include "sandbox/src/policy_engine_params.h" +#include "base/basictypes.h" + +// The low-level policy is implemented using the concept of policy 'opcodes'. +// An opcode is a structure that contains enough information to perform one +// comparison against one single input parameter. For example, an opcode can +// encode just one of the following comparison: +// +// - Is input parameter 3 not equal to NULL? +// - Does input parameter 2 start with L"c:\\"? +// - Is input parameter 5, bit 3 is equal 1? +// +// Each opcode is in fact equivalent to a function invocation where all +// the parameters are known by the opcode except one. So say you have a +// function of this form: +// bool fn(a, b, c, d) with 4 arguments +// +// Then an opcode is: +// op(fn, b, c, d) +// Which stores the function to call and its 3 last arguments +// +// Then and opcode evaluation is: +// op.eval(a) ------------------------> fn(a,b,c,d) +// internally calls +// +// The idea is that complex policy rules can be split into streams of +// opcodes which are evaluated in sequence. The evaluation is done in +// groups of opcodes that have N comparison opcodes plus 1 action opcode: +// +// [comparison 1][comparison 2]...[comparison N][action][comparison 1]... +// ----- evaluation order-----------> +// +// Each opcode group encodes one high-level policy rule. The rule applies +// only if all the conditions on the group evaluate to true. The action +// opcode contains the policy outcome for that particular rule. +// +// Note that this header contains the main building blocks of low-level policy +// but not the low level policy class. +namespace sandbox { + +// These are the possible policy outcomes. Note that some of them might +// not apply and can be removed. Also note that The following values only +// specify what to do, not how to do it and it is acceptable given specific +// cases to ignore the policy outcome. +enum EvalResult { + // Comparison opcode values: + EVAL_TRUE, // Opcode condition evaluated true. + EVAL_FALSE, // Opcode condition evaluated false. + EVAL_ERROR, // Opcode condition generated an error while evaluating. + // Action opcode values: + ASK_BROKER, // The target must generate an IPC to the broker. On the broker + // side, this means grant access to the resource. + DENY_ACCESS, // No access granted to the resource. + GIVE_READONLY, // Give readonly access to the resource. + GIVE_ALLACCESS, // Give full access to the resource. + GIVE_CACHED, // IPC is not required. Target can return a cached handle. + GIVE_FIRST, // TODO(cpu) + SIGNAL_ALARM, // Unusual activity. Generate an alarm. + FAKE_SUCCESS, // Do not call original function. Just return 'success'. + FAKE_ACCESS_DENIED, // Do not call original function. Just return 'denied' + // and do not do IPC. + TERMINATE_PROCESS, // Destroy target process. Do IPC as well. +}; + +// The following are the implemented opcodes. +enum OpcodeID { + OP_ALWAYS_FALSE, // Evaluates to false (EVAL_FALSE). + OP_ALWAYS_TRUE, // Evaluates to true (EVAL_TRUE). + OP_NUMBER_MATCH, // Match a 32-bit integer as n == a. + OP_ULONG_MATCH_RANGE, // Match an ulong integer as a <= n <= b. + OP_ULONG_AND_MATCH, // Match using bitwise AND; as in: n & a != 0. + OP_WSTRING_MATCH, // Match a string for equality. + OP_ACTION // Evaluates to an action opcode. +}; + +// Options that apply to every opcode. They are specified when creating +// each opcode using OpcodeFactory::MakeOpXXXXX() family of functions +// Do nothing special. +const uint32 kPolNone = 0; + +// Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express +// negated conditions such as if ( a && !b). +const uint32 kPolNegateEval = 1; + +// Zero the MatchContext context structure. This happens after the opcode +// is evaluated. +const uint32 kPolClearContext = 2; + +// Use OR when evaluating this set of opcodes. The policy evaluator by default +// uses AND when evaluating. Very helpful when +// used with kPolNegateEval. For example if you have a condition best expressed +// as if(! (a && b && c)), the use of this flags allows it to be expressed as +// if ((!a) || (!b) || (!c)). +const uint32 kPolUseOREval = 4; + +// Keeps the evaluation state between opcode evaluations. This is used +// for string matching where the next opcode needs to continue matching +// from the last character position from the current opcode. The match +// context is preserved across opcode evaluation unless an opcode specifies +// as an option kPolClearContext. +struct MatchContext { + size_t position; + uint32 options; + + MatchContext() { + Clear(); + } + + void Clear() { + position = 0; + options = 0; + } +}; + +// Models a policy opcode; that is a condition evaluation were all the +// arguments but one are stored in objects of this class. Use OpcodeFactory +// to create objects of this type. +// This class is just an implementation artifact and not exposed to the +// API clients or visible in the intercepted service. Internally, an +// opcode is just: +// - An integer that identifies the actual opcode. +// - An index to indicate which one is the input argument +// - An array of arguments. +// While an OO hierarchy of objects would have been a natural choice, the fact +// that 1) this code can execute before the CRT is loaded, presents serious +// problems in terms of guarantees about the actual state of the vtables and +// 2) because the opcode objects are generated in the broker process, we need to +// use plain objects. To preserve some minimal type safety templates are used +// when possible. +class PolicyOpcode { + friend class OpcodeFactory; + public: + // Evaluates the opcode. For a typical comparison opcode the return value + // is EVAL_TRUE or EVAL_FALSE. If there was an error in the evaluation the + // the return is EVAL_ERROR. If the opcode is an action opcode then the + // return can take other values such as ASK_BROKER. + // parameters: An array of all input parameters. This argument is normally + // created by the macros POLPARAMS_BEGIN() POLPARAMS_END. + // count: The number of parameters passed as first argument. + // match: The match context that is persisted across the opcode evaluation + // sequence. + EvalResult Evaluate(const ParameterSet* parameters, size_t count, + MatchContext* match); + + // Retrieves a stored argument by index. Valid index values are + // from 0 to < kArgumentCount. + template + void GetArgument(size_t index, T* argument) const { + COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size); + *argument = *reinterpret_cast(&arguments_[index].mem); + } + + // Sets a stored argument by index. Valid index values are + // from 0 to < kArgumentCount. + template + void SetArgument(size_t index, const T& argument) { + COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size); + *reinterpret_cast(&arguments_[index].mem) = argument; + } + + // Retrieves the actual address of an string argument. When using + // GetArgument() to retrieve an index that contains a string, the returned + // value is just an offset to the actual string. + // index: the stored string index. Valid values are from 0 + // to < kArgumentCount. + const wchar_t* GetRelativeString(size_t index) const { + ptrdiff_t str_delta = 0; + GetArgument(index, &str_delta); + const char* delta = reinterpret_cast(this) + str_delta; + return reinterpret_cast(delta); + } + + // Returns true if this opcode is an action opcode without actually + // evaluating it. Used to do a quick scan forward to the next opcode group. + bool IsAction() const { + return (OP_ACTION == opcode_id_); + }; + + // Returns the opcode type. + OpcodeID GetID() const { + return opcode_id_; + } + + // Returns the stored options such as kPolNegateEval and others. + uint32 GetOptions() const { + return options_; + } + + // Sets the stored options such as kPolNegateEval. + void SetOptions(int16 options) { + options_ = options; + } + + private: + + static const size_t kArgumentCount = 4; // The number of supported argument. + + struct OpcodeArgument { + UINT_PTR mem; + }; + + // Better define placement new in the class instead of relying on the + // global definition which seems to be fubared. + void* operator new(size_t, void* location) { + return location; + } + + // Helper function to evaluate the opcode. The parameters have the same + // meaning that in Evaluate(). + EvalResult EvaluateHelper(const ParameterSet* parameters, + MatchContext* match); + OpcodeID opcode_id_; + int16 parameter_; + int16 options_; + OpcodeArgument arguments_[PolicyOpcode::kArgumentCount]; +}; + +enum StringMatchOptions { + CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by + CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API. + EXACT_LENGHT = 2 // Don't do substring match. Do full string match. +}; + +// Opcodes that do string comparisons take a parameter that is the starting +// position to perform the comparison so we can do substring matching. There +// are two special values: +// +// Start from the current position and compare strings advancing forward until +// a match is found if any. Similar to CRT strstr(). +const int kSeekForward = -1; +// Perform a match with the end of the string. It only does a single comparison. +const int kSeekToEnd = 0xfffff; + + +// A PolicyBuffer is a variable size structure that contains all the opcodes +// that are to be created or evaluated in sequence. +struct PolicyBuffer { + size_t opcode_count; + PolicyOpcode opcodes[1]; +}; + +// Helper class to create any opcode sequence. This class is normally invoked +// only by the high level policy module or when you need to handcraft a special +// policy. +// The factory works by creating the opcodes using a chunk of memory given +// in the constructor. The opcodes themselves are allocated from the beginning +// (top) of the memory, while any string that an opcode needs is allocated from +// the end (bottom) of the memory. +// +// In essence: +// +// low address ---> [opcode 1] +// [opcode 2] +// [opcode 3] +// | | <--- memory_top_ +// | free | +// | | +// | | <--- memory_bottom_ +// [string 1] +// high address --> [string 2] +// +// Note that this class does not keep track of the number of opcodes made and +// it is designed to be a building block for low-level policy. +// +// Note that any of the MakeOpXXXXX member functions below can return NULL on +// failure. When that happens opcode sequence creation must be aborted. +class OpcodeFactory { + public: + // memory: base pointer to a chunk of memory where the opcodes are created. + // memory_size: the size in bytes of the memory chunk. + OpcodeFactory(char* memory, size_t memory_size) + : memory_top_(memory) { + memory_bottom_ = &memory_top_[memory_size]; + } + + // policy: contains the raw memory where the opcodes are created. + // memory_size: contains the actual size of the policy argument. + OpcodeFactory(PolicyBuffer* policy, size_t memory_size) { + memory_top_ = reinterpret_cast(&policy->opcodes[0]); + memory_bottom_ = &memory_top_[memory_size]; + } + + // Creates an OpAlwaysFalse opcode. + PolicyOpcode* MakeOpAlwaysFalse(uint32 options); + + // Creates an OpAlwaysFalse opcode. + PolicyOpcode* MakeOpAlwaysTrue(uint32 options); + + // Creates an OpAction opcode. + // action: The action to return when Evaluate() is called. + PolicyOpcode* MakeOpAction(EvalResult action, uint32 options); + + // Creates an OpNumberMatch opcode. + // selected_param: index of the input argument. It must be a ulong or the + // evaluation result will generate a EVAL_ERROR. + // match: the number to compare against the selected_param. + PolicyOpcode* MakeOpNumberMatch(int16 selected_param, unsigned long match, + uint32 options); + + // Creates an OpNumberMatch opcode (void pointers are cast to numbers). + // selected_param: index of the input argument. It must be an void* or the + // evaluation result will generate a EVAL_ERROR. + // match: the pointer numeric value to compare against selected_param. + PolicyOpcode* MakeOpVoidPtrMatch(int16 selected_param, const void* match, + uint32 options); + + // Creates an OpUlongMatchRange opcode using the memory passed in the ctor. + // selected_param: index of the input argument. It must be a ulong or the + // evaluation result will generate a EVAL_ERROR. + // lower_bound, upper_bound: the range to compare against selected_param. + PolicyOpcode* MakeOpUlongMatchRange(int16 selected_param, + unsigned long lower_bound, + unsigned long upper_bound, + uint32 options); + + // Creates an OpWStringMatch opcode using the raw memory passed in the ctor. + // selected_param: index of the input argument. It must be a wide string + // pointer or the evaluation result will generate a EVAL_ERROR. + // match_str: string to compare against selected_param. + // start_position: when its value is from 0 to < 0x7fff it indicates an + // offset from the selected_param string where to perform the comparison. If + // the value is SeekForward then a substring search is performed. If the + // value is SeekToEnd the comparison is performed against the last part of + // the selected_param string. + // Note that the range in the position (0 to 0x7fff) is dictated by the + // current implementation. + // match_opts: Indicates additional matching flags. Currently CaseInsensitive + // is supported. + PolicyOpcode* MakeOpWStringMatch(int16 selected_param, + const wchar_t* match_str, + int start_position, + StringMatchOptions match_opts, + uint32 options); + + // Creates an OpUlongAndMatch opcode using the raw memory passed in the ctor. + // selected_param: index of the input argument. It must be ulong or the + // evaluation result will generate a EVAL_ERROR. + // match: the value to bitwise AND against selected_param. + PolicyOpcode* MakeOpUlongAndMatch(int16 selected_param, + unsigned long match, + uint32 options); + + private: + // Constructs the common part of every opcode. selected_param is the index + // of the input param to use when evaluating the opcode. Pass -1 in + // selected_param to indicate that no input parameter is required. + PolicyOpcode* MakeBase(OpcodeID opcode_id, uint32 options, + int16 selected_param); + + // Allocates (and copies) a string (of size length) inside the buffer and + // returns the displacement with respect to start. + ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t lenght); + + // Returns the available memory to make opcodes. + size_t memory_size() const { + return memory_bottom_ - memory_top_; + } + + // Points to the lowest currently available address of the memory + // used to make the opcodes. This pointer increments as opcodes are made. + char* memory_top_; + + // Points to the highest currently available address of the memory + // used to make the opcodes. This pointer decrements as opcode strings are + // allocated. + char* memory_bottom_; + + DISALLOW_COPY_AND_ASSIGN(OpcodeFactory); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_POLICY_ENGINE_OPCODES_H__ diff --git a/sandbox/win/src/policy_engine_params.h b/sandbox/win/src/policy_engine_params.h new file mode 100644 index 0000000..ecf454f --- /dev/null +++ b/sandbox/win/src/policy_engine_params.h @@ -0,0 +1,202 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__ +#define SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__ + +#include "base/basictypes.h" +#include "sandbox/src/internal_types.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_nt_util.h" + +// This header defines the classes that allow the low level policy to select +// the input parameters. In order to better make sense of this header is +// recommended that you check policy_engine_opcodes.h first. + +namespace sandbox { + +// Models the set of interesting parameters of an intercepted system call +// normally you don't create objects of this class directly, instead you +// use the POLPARAMS_XXX macros. +// For example, if an intercepted function has the following signature: +// +// NTSTATUS NtOpenFileFunction (PHANDLE FileHandle, +// ACCESS_MASK DesiredAccess, +// POBJECT_ATTRIBUTES ObjectAttributes, +// PIO_STATUS_BLOCK IoStatusBlock, +// ULONG ShareAccess, +// ULONG OpenOptions); +// +// You could say that the following parameters are of interest to policy: +// +// POLPARAMS_BEGIN(open_params) +// POLPARAM(DESIRED_ACCESS) +// POLPARAM(OBJECT_NAME) +// POLPARAM(SECURITY_DESCRIPTOR) +// POLPARAM(IO_STATUS) +// POLPARAM(OPEN_OPTIONS) +// POLPARAMS_END; +// +// and the actual code will use this for defining the parameters: +// +// CountedParameterSet p; +// p[open_params::DESIRED_ACCESS] = ParamPickerMake(DesiredAccess); +// p[open_params::OBJECT_NAME] = +// ParamPickerMake(ObjectAttributes->ObjectName); +// p[open_params::SECURITY_DESCRIPTOR] = +// ParamPickerMake(ObjectAttributes->SecurityDescriptor); +// p[open_params::IO_STATUS] = ParamPickerMake(IoStatusBlock); +// p[open_params::OPEN_OPTIONS] = ParamPickerMake(OpenOptions); +// +// These will create an stack-allocated array of ParameterSet objects which +// have each 1) the address of the parameter 2) a numeric id that encodes the +// original C++ type. This allows the policy to treat any set of supported +// argument types uniformily and with some type safety. +// +// TODO(cpu): support not fully implemented yet for unicode string and will +// probably add other types as well. +class ParameterSet { + public: + ParameterSet() : real_type_(INVALID_TYPE), address_(NULL) {} + + // Retrieve the stored parameter. If the type does not match ulong fail. + bool Get(unsigned long* destination) const { + if (ULONG_TYPE != real_type_) { + return false; + } + *destination = Void2TypePointerCopy(); + return true; + } + + // Retrieve the stored parameter. If the type does not match void* fail. + bool Get(const void** destination) const { + if (VOIDPTR_TYPE != real_type_) { + return false; + } + *destination = Void2TypePointerCopy(); + return true; + } + + // Retrieve the stored parameter. If the type does not match wchar_t* fail. + bool Get(const wchar_t** destination) const { + if (WCHAR_TYPE != real_type_) { + return false; + } + *destination = Void2TypePointerCopy(); + return true; + } + + // False if the parameter is not properly initialized. + bool IsValid() const { + return INVALID_TYPE != real_type_; + } + + protected: + // The constructor can only be called by derived types, which should + // safely provide the real_type and the address of the argument. + ParameterSet(ArgType real_type, const void* address) + : real_type_(real_type), address_(address) { + } + + private: + // This template provides the same functionality as bits_cast but + // it works with pointer while the former works only with references. + template + T Void2TypePointerCopy() const { + return *(reinterpret_cast(address_)); + } + + ArgType real_type_; + const void* address_; +}; + +// To safely infer the type, we use a set of template specializations +// in ParameterSetEx with a template function ParamPickerMake to do the +// parameter type deduction. + +// Base template class. Not implemented so using unsupported types should +// fail to compile. +template +class ParameterSetEx : public ParameterSet { + public: + ParameterSetEx(const void* address); +}; + +template<> +class ParameterSetEx : public ParameterSet { + public: + ParameterSetEx(const void* address) + : ParameterSet(VOIDPTR_TYPE, address) {} +}; + +template<> +class ParameterSetEx : public ParameterSet { + public: + ParameterSetEx(const void* address) + : ParameterSet(VOIDPTR_TYPE, address) {} +}; + + +template<> +class ParameterSetEx : public ParameterSet { + public: + ParameterSetEx(const void* address) + : ParameterSet(WCHAR_TYPE, address) {} +}; + +template<> +class ParameterSetEx : public ParameterSet { + public: + ParameterSetEx(const void* address) + : ParameterSet(WCHAR_TYPE, address) {} +}; + + +template<> +class ParameterSetEx : public ParameterSet { + public: + ParameterSetEx(const void* address) + : ParameterSet(ULONG_TYPE, address) {} +}; + +template<> +class ParameterSetEx : public ParameterSet { + public: + ParameterSetEx(const void* address) + : ParameterSet(UNISTR_TYPE, address) {} +}; + +template +ParameterSet ParamPickerMake(T& parameter) { + return ParameterSetEx(¶meter); +}; + +struct CountedParameterSetBase { + int count; + ParameterSet parameters[1]; +}; + +// This template defines the actual list of policy parameters for a given +// interception. +// Warning: This template stores the address to the actual variables, in +// other words, the values are not copied. +template +struct CountedParameterSet { + CountedParameterSet() : count(T::PolParamLast) {} + + ParameterSet& operator[](typename T::Args n) { + return parameters[n]; + } + + CountedParameterSetBase* GetBase() { + return reinterpret_cast(this); + } + + int count; + ParameterSet parameters[T::PolParamLast]; +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__ diff --git a/sandbox/win/src/policy_engine_processor.cc b/sandbox/win/src/policy_engine_processor.cc new file mode 100644 index 0000000..d0455d0 --- /dev/null +++ b/sandbox/win/src/policy_engine_processor.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/policy_engine_processor.h" + +namespace sandbox { + +void PolicyProcessor::SetInternalState(size_t index, EvalResult result) { + state_.current_index_ = index; + state_.current_result_ = result; +} + +EvalResult PolicyProcessor::GetAction() const { + return state_.current_result_; +} + +// Decides if an opcode can be skipped (not evaluated) or not. The function +// takes as inputs the opcode and the current evaluation context and returns +// true if the opcode should be skipped or not and also can set keep_skipping +// to false to signal that the current instruction should be skipped but not +// the next after the current one. +bool SkipOpcode(PolicyOpcode& opcode, MatchContext* context, + bool* keep_skipping) { + if (opcode.IsAction()) { + uint32 options = context->options; + context->Clear(); + *keep_skipping = false; + return (kPolUseOREval == options)? false : true; + } + *keep_skipping = true; + return true; +} + +PolicyResult PolicyProcessor::Evaluate(uint32 options, + ParameterSet* parameters, + size_t param_count) { + if (NULL == policy_) { + return NO_POLICY_MATCH; + } + if (0 == policy_->opcode_count) { + return NO_POLICY_MATCH; + } + if (!(kShortEval & options)) { + return POLICY_ERROR; + } + + MatchContext context; + bool evaluation = false; + bool skip_group = false; + SetInternalState(0, EVAL_FALSE); + size_t count = policy_->opcode_count; + + // Loop over all the opcodes Evaluating in sequence. Since we only support + // short circuit evaluation, we stop as soon as we find an 'action' opcode + // and the current evaluation is true. + // + // Skipping opcodes can happen when we are in AND mode (!kPolUseOREval) and + // have got EVAL_FALSE or when we are in OR mode (kPolUseOREval) and got + // EVAL_TRUE. Skipping will stop at the next action opcode or at the opcode + // after the action depending on kPolUseOREval. + + for (size_t ix = 0; ix != count; ++ix) { + PolicyOpcode& opcode = policy_->opcodes[ix]; + // Skipping block. + if (skip_group) { + if (SkipOpcode(opcode, &context, &skip_group)) { + continue; + } + } + // Evaluation block. + EvalResult result = opcode.Evaluate(parameters, param_count, &context); + switch (result) { + case EVAL_FALSE: + evaluation = false; + if (kPolUseOREval != context.options) { + skip_group = true; + } + break; + case EVAL_ERROR: + if (kStopOnErrors & options) { + return POLICY_ERROR; + } + break; + case EVAL_TRUE: + evaluation = true; + if (kPolUseOREval == context.options) { + skip_group = true; + } + break; + default: + // We have evaluated an action. + SetInternalState(ix, result); + return POLICY_MATCH; + } + } + + if (evaluation) { + // Reaching the end of the policy with a positive evaluation is probably + // an error: we did not find a final action opcode? + return POLICY_ERROR; + } + return NO_POLICY_MATCH; +} + + +} // namespace sandbox diff --git a/sandbox/win/src/policy_engine_processor.h b/sandbox/win/src/policy_engine_processor.h new file mode 100644 index 0000000..3327e36 --- /dev/null +++ b/sandbox/win/src/policy_engine_processor.h @@ -0,0 +1,145 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__ +#define SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__ + +#include "base/basictypes.h" +#include "sandbox/src/policy_engine_params.h" +#include "sandbox/src/policy_engine_opcodes.h" + +namespace sandbox { + +// This header contains the core policy evaluator. In its simplest form +// it evaluates a stream of opcodes assuming that they are laid out in +// memory as opcode groups. +// +// An opcode group has N comparison opcodes plus 1 action opcode. For +// example here we have 3 opcode groups (A, B,C): +// +// [comparison 1] <-- group A start +// [comparison 2] +// [comparison 3] +// [action A ] +// [comparison 1] <-- group B start +// [action B ] +// [comparison 1] <-- group C start +// [comparison 2] +// [action C ] +// +// The opcode evaluator proceeds from the top, evaluating each opcode in +// sequence. An opcode group is evaluated until the first comparison that +// returns false. At that point the rest of the group is skipped and evaluation +// resumes with the first comparison of the next group. When all the comparisons +// in a group have evaluated to true and the action is reached. The group is +// considered a matching group. +// +// In the 'ShortEval' mode evaluation stops when it reaches the end or the first +// matching group. The action opcode from this group is the resulting policy +// action. +// +// In the 'RankedEval' mode evaluation stops only when it reaches the end of the +// the opcode stream. In the process all matching groups are saved and at the +// end the 'best' group is selected (what makes the best is TBD) and the action +// from this group is the resulting policy action. +// +// As explained above, the policy evaluation of a group is a logical AND of +// the evaluation of each opcode. However an opcode can request kPolUseOREval +// which makes the evaluation to use logical OR. Given that each opcode can +// request its evaluation result to be negated with kPolNegateEval you can +// achieve the negation of the total group evaluation. This means that if you +// need to express: +// if (!(c1 && c2 && c3)) +// You can do it by: +// if ((!c1) || (!c2) || (!c3)) +// + +// Possible outcomes of policy evaluation. +enum PolicyResult { + NO_POLICY_MATCH, + POLICY_MATCH, + POLICY_ERROR +}; + +// Policy evaluation flags +// TODO(cpu): implement the options 0 & 4. +// +// Stop evaluating as soon as an error is encountered. +const uint32 kStopOnErrors = 0; +// Ignore all non fatal opcode evaluation errors. +const uint32 kIgnoreErrors = 1; +// Short-circuit evaluation: Only evaluate until opcode group that +// evaluated to true has been found. +const uint32 kShortEval = 2; +// Discussed briefly at the policy design meeting. It will evaluate +// all rules and then return the 'best' rule that evaluated true. +const uint32 kRankedEval = 4; + +// This class evaluates a policy-opcode stream given the memory where the +// opcodes are and an input 'parameter set'. +// +// This class is designed to be callable from interception points +// as low as the NtXXXX service level (it is not currently safe, but +// it is designed to be made safe). +// +// Its usage in an interception is: +// +// POLPARAMS_BEGIN(eval_params) +// POLPARAM(param1) +// POLPARAM(param2) +// POLPARAM(param3) +// POLPARAM(param4) +// POLPARAM(param5) +// POLPARAMS_END; +// +// PolicyProcessor pol_evaluator(policy_memory); +// PolicyResult pr = pol_evaluator.Evaluate(ShortEval, eval_params, +// _countof(eval_params)); +// if (NO_POLICY_MATCH == pr) { +// EvalResult policy_action = pol_evaluator.GetAction(); +// // apply policy here... +// } +// +// Where the POLPARAM() arguments are derived from the intercepted function +// arguments, and represent all the 'interesting' policy inputs, and +// policy_memory is a memory buffer containing the opcode stream that is the +// relevant policy for this intercept. +class PolicyProcessor { + public: + // policy_buffer contains opcodes made with OpcodeFactory. They are usually + // created in the broker process and evaluated in the target process. + + // This constructor is just a variant of the previous constructor. + explicit PolicyProcessor(PolicyBuffer* policy) + : policy_(policy) { + SetInternalState(0, EVAL_FALSE); + } + + // Evaluates a policy-opcode stream. See the comments at the top of this + // class for more info. Returns POLICY_MATCH if a rule set was found that + // matches an active policy. + PolicyResult Evaluate(uint32 options, + ParameterSet* parameters, + size_t parameter_count); + + // If the result of Evaluate() was POLICY_MATCH, calling this function returns + // the recommended policy action. + EvalResult GetAction() const; + + private: + struct { + size_t current_index_; + EvalResult current_result_; + } state_; + + // Sets the currently matching action result. + void SetInternalState(size_t index, EvalResult result); + + PolicyBuffer* policy_; + DISALLOW_COPY_AND_ASSIGN(PolicyProcessor); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__ diff --git a/sandbox/win/src/policy_engine_unittest.cc b/sandbox/win/src/policy_engine_unittest.cc new file mode 100644 index 0000000..f988ccc --- /dev/null +++ b/sandbox/win/src/policy_engine_unittest.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/policy_engine_params.h" +#include "sandbox/src/policy_engine_processor.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = { +#define POLPARAM(p) sandbox::ParamPickerMake(p), +#define POLPARAMS_END } + +namespace sandbox { + +bool SetupNtdllImports(); + +TEST(PolicyEngineTest, Rules1) { + SetupNtdllImports(); + + // Construct two policy rules that say: + // + // #1 + // If the path is c:\\documents and settings\\* AND + // If the creation mode is 'open existing' AND + // If the security descriptor is null THEN + // Ask the broker. + // + // #2 + // If the security descriptor is null AND + // If the path ends with *.txt AND + // If the creation mode is not 'create new' THEN + // return Access Denied. + + enum FileCreateArgs { + FileNameArg, + CreationDispositionArg, + FlagsAndAttributesArg, + SecurityAttributes + }; + + const size_t policy_sz = 1024; + PolicyBuffer* policy = reinterpret_cast(new char[policy_sz]); + OpcodeFactory opcode_maker(policy, policy_sz - 0x40); + + // Add rule set #1 + opcode_maker.MakeOpWStringMatch(FileNameArg, + L"c:\\documents and settings\\", + 0, CASE_INSENSITIVE, kPolNone); + opcode_maker.MakeOpNumberMatch(CreationDispositionArg, OPEN_EXISTING, + kPolNone); + opcode_maker.MakeOpVoidPtrMatch(SecurityAttributes, (void*)NULL, + kPolNone); + opcode_maker.MakeOpAction(ASK_BROKER, kPolNone); + + // Add rule set #2 + opcode_maker.MakeOpWStringMatch(FileNameArg, L".TXT", + kSeekToEnd, CASE_INSENSITIVE, kPolNone); + opcode_maker.MakeOpNumberMatch(CreationDispositionArg, CREATE_NEW, + kPolNegateEval); + opcode_maker.MakeOpAction(FAKE_ACCESS_DENIED, kPolNone); + policy->opcode_count = 7; + + wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt"; + unsigned long creation_mode = OPEN_EXISTING; + unsigned long flags = FILE_ATTRIBUTE_NORMAL; + void* security_descriptor = NULL; + + POLPARAMS_BEGIN(eval_params) + POLPARAM(filename) + POLPARAM(creation_mode) + POLPARAM(flags) + POLPARAM(security_descriptor) + POLPARAMS_END; + + PolicyResult pr; + PolicyProcessor pol_ev(policy); + + // Test should match the first rule set. + pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, pr); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + // Test should still match the first rule set. + pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, pr); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + // Changing creation_mode such that evaluation should not match any rule. + creation_mode = CREATE_NEW; + pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, pr); + + // Changing creation_mode such that evaluation should match rule #2. + creation_mode = OPEN_ALWAYS; + pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, pr); + EXPECT_EQ(FAKE_ACCESS_DENIED, pol_ev.GetAction()); + + delete [] reinterpret_cast(policy); +} + +} // namespace sandbox diff --git a/sandbox/win/src/policy_low_level.cc b/sandbox/win/src/policy_low_level.cc new file mode 100644 index 0000000..401f2e1 --- /dev/null +++ b/sandbox/win/src/policy_low_level.cc @@ -0,0 +1,348 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "sandbox/src/policy_low_level.h" +#include "base/basictypes.h" + +namespace { + + // A single rule can use at most this amount of memory. + const size_t kRuleBufferSize = 1024*4; + + // The possible states of the string matching opcode generator. + enum { + PENDING_NONE, + PENDING_ASTERISK, // Have seen an '*' but have not generated an opcode. + PENDING_QMARK, // Have seen an '?' but have not generated an opcode. + }; + + // The category of the last character seen by the string matching opcode + // generator. + const uint32 kLastCharIsNone = 0; + const uint32 kLastCharIsAlpha = 1; + const uint32 kLastCharIsWild = 2; + const uint32 kLastCharIsAsterisk = kLastCharIsWild + 4; + const uint32 kLastCharIsQuestionM = kLastCharIsWild + 8; +} + +namespace sandbox { + +// Adding a rule is nothing more than pushing it into an stl container. Done() +// is called for the rule in case the code that made the rule in the first +// place has not done it. +bool LowLevelPolicy::AddRule(int service, PolicyRule* rule) { + if (!rule->Done()) { + return false; + } + + PolicyRule* local_rule = new PolicyRule(*rule); + RuleNode node = {local_rule, service}; + rules_.push_back(node); + return true; +} + +LowLevelPolicy::~LowLevelPolicy() { + // Delete all the rules. + typedef std::list RuleNodes; + for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { + delete it->rule; + } +} + +// Here is where the heavy byte shuffling is done. We take all the rules and +// 'compile' them into a single memory region. Now, the rules are in random +// order so the first step is to reorganize them into a stl map that is keyed +// by the service id and as a value contains a list with all the rules that +// belong to that service. Then we enter the big for-loop where we carve a +// memory zone for the opcodes and the data and call RebindCopy on each rule +// so they all end up nicely packed in the policy_store_. +bool LowLevelPolicy::Done() { + typedef std::list RuleNodes; + typedef std::list RuleList; + typedef std::map Mmap; + Mmap mmap; + + for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { + mmap[it->service].push_back(it->rule); + } + + PolicyBuffer* current_buffer = &policy_store_->data[0]; + char* buffer_end = reinterpret_cast(current_buffer) + + policy_store_->data_size; + size_t avail_size = policy_store_->data_size; + + for (Mmap::iterator it = mmap.begin(); it != mmap.end(); ++it) { + uint32 service = (*it).first; + if (service >= kMaxServiceCount) { + return false; + } + policy_store_->entry[service] = current_buffer; + + RuleList::iterator rules_it = (*it).second.begin(); + RuleList::iterator rules_it_end = (*it).second.end(); + + size_t svc_opcode_count = 0; + + for (; rules_it != rules_it_end; ++rules_it) { + const PolicyRule* rule = (*rules_it); + size_t op_count = rule->GetOpcodeCount(); + + size_t opcodes_size = op_count * sizeof(PolicyOpcode); + if (avail_size < opcodes_size) { + return false; + } + size_t data_size = avail_size - opcodes_size; + PolicyOpcode* opcodes_start = ¤t_buffer->opcodes[svc_opcode_count]; + if (!rule->RebindCopy(opcodes_start, opcodes_size, + buffer_end, &data_size)) { + return false; + } + size_t used = avail_size - data_size; + buffer_end -= used; + avail_size -= used; + svc_opcode_count += op_count; + } + + current_buffer->opcode_count += svc_opcode_count; + size_t policy_byte_count = (svc_opcode_count * sizeof(PolicyOpcode)) + / sizeof(current_buffer[0]); + current_buffer = ¤t_buffer[policy_byte_count + 1]; + } + + return true; +} + +PolicyRule::PolicyRule(EvalResult action) + : action_(action), done_(false) { + char* memory = new char[sizeof(PolicyBuffer) + kRuleBufferSize]; + buffer_ = reinterpret_cast(memory); + buffer_->opcode_count = 0; + opcode_factory_ = new OpcodeFactory(buffer_, + kRuleBufferSize + sizeof(PolicyOpcode)); +} + +PolicyRule::PolicyRule(const PolicyRule& other) { + if (this == &other) + return; + action_ = other.action_; + done_ = other.done_; + size_t buffer_size = sizeof(PolicyBuffer) + kRuleBufferSize; + char* memory = new char[buffer_size]; + buffer_ = reinterpret_cast(memory); + memcpy(buffer_, other.buffer_, buffer_size); + + char* opcode_buffer = reinterpret_cast(&buffer_->opcodes[0]); + char* buffer_end = &opcode_buffer[kRuleBufferSize + sizeof(PolicyOpcode)]; + char* next_opcode = &opcode_buffer[GetOpcodeCount() * sizeof(PolicyOpcode)]; + opcode_factory_ = new OpcodeFactory(next_opcode, buffer_end - next_opcode); +} + +// This function get called from a simple state machine implemented in +// AddStringMatch() which passes the current state (in state) and it passes +// true in last_call if AddStringMatch() has finished processing the input +// pattern string and this would be the last call to generate any pending +// opcode. The skip_count is the currently accumulated number of '?' seen so +// far and once the associated opcode is generated this function sets it back +// to zero. +bool PolicyRule::GenStringOpcode(RuleType rule_type, + StringMatchOptions match_opts, + uint16 parameter, int state, bool last_call, + int* skip_count, std::wstring* fragment) { + + // The last opcode must: + // 1) Always clear the context. + // 2) Preserve the negation. + // 3) Remove the 'OR' mode flag. + uint32 options = kPolNone; + if (last_call) { + if (IF_NOT == rule_type) { + options = kPolClearContext | kPolNegateEval; + } else { + options = kPolClearContext; + } + } else if (IF_NOT == rule_type) { + options = kPolUseOREval | kPolNegateEval; + } + + PolicyOpcode* op = NULL; + + // The fragment string contains the accumulated characters to match with, it + // never contains wildcards (unless they have been escaped) and while there + // is no fragment there is no new string match opcode to generate. + if (fragment->empty()) { + // There is no new opcode to generate but in the last call we have to fix + // the previous opcode because it was really the last but we did not know + // it at that time. + if (last_call && (buffer_->opcode_count > 0)) { + op = &buffer_->opcodes[buffer_->opcode_count - 1]; + op->SetOptions(options); + } + return true; + } + + if (PENDING_ASTERISK == state) { + if (last_call) { + op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), + kSeekToEnd, match_opts, + options); + } else { + op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), + kSeekForward, match_opts, + options); + } + + } else if (PENDING_QMARK == state) { + op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), + *skip_count, match_opts, options); + *skip_count = 0; + } else { + if (last_call) { + match_opts = static_cast(EXACT_LENGHT | match_opts); + } + op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 0, + match_opts, options); + } + if (NULL == op) { + return false; + } + ++buffer_->opcode_count; + fragment->clear(); + return true; +} + +bool PolicyRule::AddStringMatch(RuleType rule_type, int16 parameter, + const wchar_t* string, + StringMatchOptions match_opts) { + if (done_) { + // Do not allow to add more rules after generating the action opcode. + return false; + } + + const wchar_t* current_char = string; + uint32 last_char = kLastCharIsNone; + int state = PENDING_NONE; + int skip_count = 0; // counts how many '?' we have seen in a row. + std::wstring fragment; // accumulates the non-wildcard part of the string. + + while (L'\0' != *current_char) { + switch (*current_char) { + case L'*': + if (kLastCharIsWild & last_char) { + // '**' and '&*' is an error. + return false; + } + if (!GenStringOpcode(rule_type, match_opts, parameter, + state, false, &skip_count, &fragment)) { + return false; + } + last_char = kLastCharIsAsterisk; + state = PENDING_ASTERISK; + break; + case L'?': + if (kLastCharIsAsterisk == last_char) { + // '*?' is an error. + return false; + } + if (!GenStringOpcode(rule_type, match_opts, parameter, + state, false, &skip_count, &fragment)) { + return false; + } + ++skip_count; + last_char = kLastCharIsQuestionM; + state = PENDING_QMARK; + break; + case L'/': + // Note: "/?" is an escaped '?'. Eat the slash and fall through. + if (L'?' == current_char[1]) { + ++current_char; + } + default: + fragment += *current_char; + last_char = kLastCharIsAlpha; + } + ++current_char; + } + + if (!GenStringOpcode(rule_type, match_opts, parameter, + state, true, &skip_count, &fragment)) { + return false; + } + return true; +} + +bool PolicyRule::AddNumberMatch(RuleType rule_type, int16 parameter, + unsigned long number, RuleOp comparison_op) { + if (done_) { + // Do not allow to add more rules after generating the action opcode. + return false; + } + uint32 opts = (rule_type == IF_NOT)? kPolNegateEval : kPolNone; + + if (EQUAL == comparison_op) { + if (NULL == opcode_factory_->MakeOpNumberMatch(parameter, number, opts)) { + return false; + } + } else if (AND == comparison_op) { + if (NULL == opcode_factory_->MakeOpUlongAndMatch(parameter, number, opts)) { + return false; + } + } + ++buffer_->opcode_count; + return true; +} + +bool PolicyRule::Done() { + if (done_) { + return true; + } + if (NULL == opcode_factory_->MakeOpAction(action_, kPolNone)) { + return false; + } + ++buffer_->opcode_count; + done_ = true; + return true; +} + +bool PolicyRule::RebindCopy(PolicyOpcode* opcode_start, size_t opcode_size, + char* data_start, size_t* data_size) const { + size_t count = buffer_->opcode_count; + for (size_t ix = 0; ix != count; ++ix) { + if (opcode_size < sizeof(PolicyOpcode)) { + return false; + } + PolicyOpcode& opcode = buffer_->opcodes[ix]; + *opcode_start = opcode; + if (OP_WSTRING_MATCH == opcode.GetID()) { + // For this opcode argument 0 is a delta to the string and argument 1 + // is the length (in chars) of the string. + const wchar_t* str = opcode.GetRelativeString(0); + size_t str_len; + opcode.GetArgument(1, &str_len); + str_len = str_len * sizeof(wchar_t); + if ((*data_size) < str_len) { + return false; + } + *data_size -= str_len; + data_start -= str_len; + memcpy(data_start, str, str_len); + // Recompute the string displacement + ptrdiff_t delta = data_start - reinterpret_cast(opcode_start); + opcode_start->SetArgument(0, delta); + } + ++opcode_start; + opcode_size -= sizeof(PolicyOpcode); + } + + return true; +} + +PolicyRule::~PolicyRule() { + delete [] reinterpret_cast(buffer_); + delete opcode_factory_; +} + +} // namespace sandbox diff --git a/sandbox/win/src/policy_low_level.h b/sandbox/win/src/policy_low_level.h new file mode 100644 index 0000000..5bf6a46 --- /dev/null +++ b/sandbox/win/src/policy_low_level.h @@ -0,0 +1,182 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_POLICY_LOW_LEVEL_H__ +#define SANDBOX_SRC_POLICY_LOW_LEVEL_H__ + +#include + +#include "base/basictypes.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_engine_params.h" +#include "sandbox/src/policy_engine_opcodes.h" + +// Low level policy classes. +// Built on top of the PolicyOpcode and OpcodeFatory, the low level policy +// provides a way to define rules on strings and numbers but it is unaware +// of Windows specific details or how the Interceptions must be set up. +// To use these classes you construct one or more rules and add them to the +// LowLevelPolicy object like this: +// +// PolicyRule rule1(ASK_BROKER); +// rule1.AddStringMatch(IF, 0, L"\\\\/?/?\\c:\\*Microsoft*\\*.exe", true); +// rule1.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL); +// rule1.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL); +// +// PolicyRule rule2(FAKE_SUCCESS); +// rule2.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*", false)); +// rule2.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL)); +// +// LowLevelPolicy policyGen(*policy_memory); +// policyGen.AddRule(kNtCreateFileSvc, &rule1); +// policyGen.AddRule(kNtCreateFileSvc, &rule2); +// policyGen.Done(); +// +// At this point (error checking omitted) the policy_memory can be copied +// to the target process where it can be evaluated. + +namespace sandbox { + +// TODO(cpu): Move this constant to crosscall_client.h. +const size_t kMaxServiceCount = 32; +COMPILE_ASSERT(IPC_LAST_TAG <= kMaxServiceCount, kMaxServiceCount_is_too_low); + +// Defines the memory layout of the policy. This memory is filled by +// LowLevelPolicy object. +// For example: +// +// [Service 0] --points to---\ +// [Service 1] --------------|-----\ +// ...... | | +// [Service N] | | +// [data_size] | | +// [Policy Buffer 0] <-------/ | +// [opcodes of] | +// ....... | +// [Policy Buffer 1] <-------------/ +// [opcodes] +// ....... +// ....... +// [Policy Buffer N] +// [opcodes] +// ....... +// +// ....... +// [opcode string ] +// [opcode string ] +// ....... +// [opcode string ] +struct PolicyGlobal { + PolicyBuffer* entry[kMaxServiceCount]; + size_t data_size; + PolicyBuffer data[1]; +}; + +class PolicyRule; + +// Provides the means to collect rules into a policy store (memory) +class LowLevelPolicy { + public: + // policy_store: must contain allocated memory and the internal + // size fields set to correct values. + explicit LowLevelPolicy(PolicyGlobal* policy_store) + : policy_store_(policy_store) { + } + + // Destroys all the policy rules. + ~LowLevelPolicy(); + + // Adds a rule to be generated when Done() is called. + // service: The id of the service that this rule is associated with, + // for example the 'Open Thread' service or the "Create File" service. + // returns false on error. + bool AddRule(int service, PolicyRule* rule); + + // Generates all the rules added with AddRule() into the memory area + // passed on the constructor. Returns false on error. + bool Done(); + + private: + struct RuleNode { + const PolicyRule* rule; + int service; + }; + std::list rules_; + PolicyGlobal* policy_store_; + DISALLOW_IMPLICIT_CONSTRUCTORS(LowLevelPolicy); +}; + +// There are 'if' rules and 'if not' comparisons +enum RuleType { + IF = 0, + IF_NOT = 1, +}; + +// Possible comparisons for numbers +enum RuleOp { + EQUAL, + AND, + RANGE // TODO(cpu): Implement this option. +}; + +// Provides the means to collect a set of comparisons into a single +// rule and its associated action. +class PolicyRule { + friend class LowLevelPolicy; + + public: + explicit PolicyRule(EvalResult action); + PolicyRule(const PolicyRule& other); + ~PolicyRule(); + + // Adds a string comparison to the rule. + // rule_type: possible values are IF and IF_NOT. + // parameter: the expected index of the argument for this rule. For example + // in a 'create file' service the file name argument can be at index 0. + // string: is the desired matching pattern. + // match_opts: if the pattern matching is case sensitive or not. + bool AddStringMatch(RuleType rule_type, int16 parameter, + const wchar_t* string, StringMatchOptions match_opts); + + // Adds a number match comparison to the rule. + // rule_type: possible values are IF and IF_NOT. + // parameter: the expected index of the argument for this rule. + // number: the value to compare the input to. + // comparison_op: the comparison kind (equal, logical and, etc). + bool AddNumberMatch(RuleType rule_type, int16 parameter, + unsigned long number, RuleOp comparison_op); + + // Returns the number of opcodes generated so far. + size_t GetOpcodeCount() const { + return buffer_->opcode_count; + } + + // Called when there is no more comparisons to add. Internally it generates + // the last opcode (the action opcode). Returns false if this operation fails. + bool Done(); + + private: + void operator=(const PolicyRule&); + // Called in a loop from AddStringMatch to generate the required string + // match opcodes. rule_type, match_opts and parameter are the same as + // in AddStringMatch. + bool GenStringOpcode(RuleType rule_type, StringMatchOptions match_opts, + uint16 parameter, int state, bool last_call, + int* skip_count, std::wstring* fragment); + + // Loop over all generated opcodes and copy them to increasing memory + // addresses from opcode_start and copy the extra data (strings usually) into + // decreasing addresses from data_start. Extra data is only present in the + // string evaluation opcodes. + bool RebindCopy(PolicyOpcode* opcode_start, size_t opcode_size, + char* data_start, size_t* data_size) const; + PolicyBuffer* buffer_; + OpcodeFactory* opcode_factory_; + EvalResult action_; + bool done_; +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_POLICY_LOW_LEVEL_H__ diff --git a/sandbox/win/src/policy_low_level_unittest.cc b/sandbox/win/src/policy_low_level_unittest.cc new file mode 100644 index 0000000..20d1777 --- /dev/null +++ b/sandbox/win/src/policy_low_level_unittest.cc @@ -0,0 +1,575 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/policy_engine_params.h" +#include "sandbox/src/policy_engine_processor.h" +#include "sandbox/src/policy_low_level.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = { +#define POLPARAM(p) sandbox::ParamPickerMake(p), +#define POLPARAMS_END } + +namespace sandbox { + +bool SetupNtdllImports(); + +// Testing that we allow opcode generation on valid string patterns. +TEST(PolicyEngineTest, StringPatternsOK) { + SetupNtdllImports(); + PolicyRule pr(ASK_BROKER); + EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\adobe\\ver??\\", CASE_SENSITIVE)); + EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"*.tmp", CASE_SENSITIVE)); + EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\*.doc", CASE_SENSITIVE)); + EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\windows\\*", CASE_SENSITIVE)); + EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"d:\\adobe\\acrobat.exe", + CASE_SENSITIVE)); +} + +// Testing that we signal invalid string patterns. +TEST(PolicyEngineTest, StringPatternsBAD) { + SetupNtdllImports(); + PolicyRule pr(ASK_BROKER); + EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"one**two", CASE_SENSITIVE)); + EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"**three", CASE_SENSITIVE)); + EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"five?six*?seven", CASE_SENSITIVE)); + EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"eight?*nine", CASE_SENSITIVE)); +} + +// Helper function to allocate space (on the heap) for policy. +PolicyGlobal* MakePolicyMemory() { + const size_t kTotalPolicySz = 4096*8; + char* mem = new char[kTotalPolicySz]; + memset(mem, 0, kTotalPolicySz); + PolicyGlobal* policy = reinterpret_cast(mem); + policy->data_size = kTotalPolicySz - sizeof(PolicyGlobal); + return policy; +} + +// The simplest test using LowLevelPolicy it should test a single opcode which +// does a exact string comparison. +TEST(PolicyEngineTest, SimpleStrMatch) { + SetupNtdllImports(); + PolicyRule pr(ASK_BROKER); + EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"z:\\Directory\\domo.txt", + CASE_INSENSITIVE)); + + PolicyGlobal* policy = MakePolicyMemory(); + const uint32 kFakeService = 2; + + LowLevelPolicy policyGen(policy); + EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); + EXPECT_TRUE(policyGen.Done()); + + wchar_t* filename = L"Z:\\Directory\\domo.txt"; + + POLPARAMS_BEGIN(eval_params) + POLPARAM(filename) // Argument 0 + POLPARAMS_END; + + PolicyResult result; + PolicyProcessor pol_ev(policy->entry[kFakeService]); + + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + filename = L"Z:\\Directory\\domo.txt.tmp"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + delete [] reinterpret_cast(policy); +} + +TEST(PolicyEngineTest, SimpleIfNotStrMatch) { + SetupNtdllImports(); + PolicyRule pr(ASK_BROKER); + EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\", + CASE_SENSITIVE)); + + PolicyGlobal* policy = MakePolicyMemory(); + const uint32 kFakeService = 2; + LowLevelPolicy policyGen(policy); + + EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); + EXPECT_TRUE(policyGen.Done()); + + wchar_t* filename = NULL; + POLPARAMS_BEGIN(eval_params) + POLPARAM(filename) // Argument 0 + POLPARAMS_END; + + PolicyResult result; + PolicyProcessor pol_ev(policy->entry[kFakeService]); + + filename = L"c:\\Microsoft\\"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\MicroNerd\\"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + filename = L"c:\\Microsoft\\domo.txt"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + delete [] reinterpret_cast(policy); +} + +TEST(PolicyEngineTest, SimpleIfNotStrMatchWild1) { + SetupNtdllImports(); + PolicyRule pr(ASK_BROKER); + EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", + CASE_SENSITIVE)); + + PolicyGlobal* policy = MakePolicyMemory(); + const uint32 kFakeService = 3; + LowLevelPolicy policyGen(policy); + + EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); + EXPECT_TRUE(policyGen.Done()); + + wchar_t* filename = NULL; + POLPARAMS_BEGIN(eval_params) + POLPARAM(filename) // Argument 0 + POLPARAMS_END; + + PolicyResult result; + PolicyProcessor pol_ev(policy->entry[kFakeService]); + + filename = L"c:\\Microsoft\\domo.txt"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\MicroNerd\\domo.txt"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + delete [] reinterpret_cast(policy); +} + +TEST(PolicyEngineTest, SimpleIfNotStrMatchWild2) { + SetupNtdllImports(); + PolicyRule pr(ASK_BROKER); + EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*.txt", + CASE_SENSITIVE)); + + PolicyGlobal* policy = MakePolicyMemory(); + const uint32 kFakeService = 3; + LowLevelPolicy policyGen(policy); + + EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); + EXPECT_TRUE(policyGen.Done()); + + wchar_t* filename = NULL; + POLPARAMS_BEGIN(eval_params) + POLPARAM(filename) // Argument 0 + POLPARAMS_END; + + PolicyResult result; + PolicyProcessor pol_ev(policy->entry[kFakeService]); + + filename = L"c:\\Microsoft\\domo.txt"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\MicroNerd\\domo.txt"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + filename = L"c:\\Microsoft\\domo.bmp"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + delete [] reinterpret_cast(policy); +} + +TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild1) { + SetupNtdllImports(); + PolicyRule pr(ASK_BROKER); + EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", + CASE_SENSITIVE)); + EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL)); + + PolicyGlobal* policy = MakePolicyMemory(); + const uint32 kFakeService = 3; + LowLevelPolicy policyGen(policy); + + EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); + EXPECT_TRUE(policyGen.Done()); + + wchar_t* filename = NULL; + unsigned long access = 0; + POLPARAMS_BEGIN(eval_params) + POLPARAM(filename) // Argument 0 + POLPARAM(access) // Argument 1 + POLPARAMS_END; + + PolicyResult result; + PolicyProcessor pol_ev(policy->entry[kFakeService]); + + filename = L"c:\\Microsoft\\domo.txt"; + access = 24; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\Microsoft\\domo.txt"; + access = 42; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\MicroNerd\\domo.txt"; + access = 24; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + filename = L"c:\\Micronesia\\domo.txt"; + access = 42; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + delete [] reinterpret_cast(policy); +} + +TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild2) { + SetupNtdllImports(); + PolicyRule pr(ASK_BROKER); + EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL)); + EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\GoogleV?\\*.txt", + CASE_SENSITIVE)); + EXPECT_TRUE(pr.AddNumberMatch(IF, 2, 66, EQUAL)); + + PolicyGlobal* policy = MakePolicyMemory(); + const uint32 kFakeService = 3; + LowLevelPolicy policyGen(policy); + + EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); + EXPECT_TRUE(policyGen.Done()); + + wchar_t* filename = NULL; + unsigned long access = 0; + unsigned long sharing = 66; + + POLPARAMS_BEGIN(eval_params) + POLPARAM(filename) // Argument 0 + POLPARAM(access) // Argument 1 + POLPARAM(sharing) // Argument 2 + POLPARAMS_END; + + PolicyResult result; + PolicyProcessor pol_ev(policy->entry[kFakeService]); + + filename = L"c:\\GoogleV2\\domo.txt"; + access = 24; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\GoogleV2\\domo.bmp"; + access = 24; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + filename = L"c:\\GoogleV23\\domo.txt"; + access = 24; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + + filename = L"c:\\GoogleV2\\domo.txt"; + access = 42; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\Google\\domo.txt"; + access = 24; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + filename = L"c:\\Micronesia\\domo.txt"; + access = 42; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\GoogleV2\\domo.bmp"; + access = 24; + sharing = 0; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + delete [] reinterpret_cast(policy); +} + +// Testing one single rule in one single service. The service is made to +// resemble NtCreateFile. +TEST(PolicyEngineTest, OneRuleTest) { + SetupNtdllImports(); + PolicyRule pr(ASK_BROKER); + EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\*Microsoft*\\*.txt", + CASE_SENSITIVE)); + EXPECT_TRUE(pr.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL)); + EXPECT_TRUE(pr.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); + + PolicyGlobal* policy = MakePolicyMemory(); + + const uint32 kNtFakeCreateFile = 7; + + LowLevelPolicy policyGen(policy); + EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr)); + EXPECT_TRUE(policyGen.Done()); + + wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt"; + unsigned long creation_mode = OPEN_EXISTING; + unsigned long flags = FILE_ATTRIBUTE_NORMAL; + void* security_descriptor = NULL; + + POLPARAMS_BEGIN(eval_params) + POLPARAM(filename) // Argument 0 + POLPARAM(creation_mode) // Argument 1 + POLPARAM(flags) // Argument 2 + POLPARAM(security_descriptor) + POLPARAMS_END; + + PolicyResult result; + PolicyProcessor pol_ev(policy->entry[kNtFakeCreateFile]); + + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + creation_mode = CREATE_ALWAYS; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + creation_mode = OPEN_EXISTING; + filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt.tmp"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + flags = FILE_ATTRIBUTE_DEVICE; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\Other\\Macrosoft\\Another file.txt"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\Microsoft\\1.txt"; + flags = FILE_ATTRIBUTE_NORMAL; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, pol_ev.GetAction()); + + filename = L"c:\\Microsoft\\1.ttt"; + result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + delete [] reinterpret_cast(policy); +} + +// Testing 3 rules in 3 services. Two of the services resemble File services. +TEST(PolicyEngineTest, ThreeRulesTest) { + SetupNtdllImports(); + PolicyRule pr_pipe(FAKE_SUCCESS); + EXPECT_TRUE(pr_pipe.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*", + CASE_INSENSITIVE)); + EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL)); + EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); + + size_t opc1 = pr_pipe.GetOpcodeCount(); + EXPECT_EQ(3, opc1); + + PolicyRule pr_dump(ASK_BROKER); + EXPECT_TRUE(pr_dump.AddStringMatch(IF, 0, L"\\\\/?/?\\*\\Crash Reports\\*", + CASE_INSENSITIVE)); + EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 1, CREATE_ALWAYS, EQUAL)); + EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); + + size_t opc2 = pr_dump.GetOpcodeCount(); + EXPECT_EQ(4, opc2); + + PolicyRule pr_winexe(SIGNAL_ALARM); + EXPECT_TRUE(pr_winexe.AddStringMatch(IF, 0, L"\\\\/?/?\\C:\\Windows\\*.exe", + CASE_INSENSITIVE)); + EXPECT_TRUE(pr_winexe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); + + size_t opc3 = pr_winexe.GetOpcodeCount(); + EXPECT_EQ(3, opc3); + + PolicyRule pr_adobe(GIVE_CACHED); + EXPECT_TRUE(pr_adobe.AddStringMatch(IF, 0, L"c:\\adobe\\ver?.?\\", + CASE_SENSITIVE)); + EXPECT_TRUE(pr_adobe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); + + size_t opc4 = pr_adobe.GetOpcodeCount(); + EXPECT_EQ(4, opc4); + + PolicyRule pr_none(GIVE_FIRST); + EXPECT_TRUE(pr_none.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_READONLY, AND)); + EXPECT_TRUE(pr_none.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_SYSTEM, AND)); + + size_t opc5 = pr_none.GetOpcodeCount(); + EXPECT_EQ(2, opc5); + + PolicyGlobal* policy = MakePolicyMemory(); + + const uint32 kNtFakeNone = 4; + const uint32 kNtFakeCreateFile = 5; + const uint32 kNtFakeOpenFile = 6; + + LowLevelPolicy policyGen(policy); + EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_pipe)); + EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_dump)); + EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_winexe)); + + EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_adobe)); + EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_pipe)); + + EXPECT_TRUE(policyGen.AddRule(kNtFakeNone, &pr_none)); + + EXPECT_TRUE(policyGen.Done()); + + // Inspect the policy structure manually. + EXPECT_TRUE(NULL == policy->entry[0]); + EXPECT_TRUE(NULL == policy->entry[1]); + EXPECT_TRUE(NULL == policy->entry[2]); + EXPECT_TRUE(NULL == policy->entry[3]); + EXPECT_TRUE(NULL != policy->entry[4]); // kNtFakeNone. + EXPECT_TRUE(NULL != policy->entry[5]); // kNtFakeCreateFile. + EXPECT_TRUE(NULL != policy->entry[6]); // kNtFakeOpenFile. + EXPECT_TRUE(NULL == policy->entry[7]); + + // The total per service opcode counts now must take in account one + // extra opcode (action opcode) per rule. + ++opc1; + ++opc2; + ++opc3; + ++opc4; + ++opc5; + + size_t tc1 = policy->entry[kNtFakeNone]->opcode_count; + size_t tc2 = policy->entry[kNtFakeCreateFile]->opcode_count; + size_t tc3 = policy->entry[kNtFakeOpenFile]->opcode_count; + + EXPECT_EQ(opc5, tc1); + EXPECT_EQ((opc1 + opc2 + opc3), tc2); + EXPECT_EQ((opc1 + opc4), tc3); + + // Check the type of the first and last opcode of each service. + + EXPECT_EQ(OP_ULONG_AND_MATCH, policy->entry[kNtFakeNone]->opcodes[0].GetID()); + EXPECT_EQ(OP_ACTION, policy->entry[kNtFakeNone]->opcodes[tc1-1].GetID()); + EXPECT_EQ(OP_WSTRING_MATCH, + policy->entry[kNtFakeCreateFile]->opcodes[0].GetID()); + EXPECT_EQ(OP_ACTION, + policy->entry[kNtFakeCreateFile]->opcodes[tc2-1].GetID()); + EXPECT_EQ(OP_WSTRING_MATCH, + policy->entry[kNtFakeOpenFile]->opcodes[0].GetID()); + EXPECT_EQ(OP_ACTION, policy->entry[kNtFakeOpenFile]->opcodes[tc3-1].GetID()); + + // Test the policy evaluation. + + wchar_t* filename = L""; + unsigned long creation_mode = OPEN_EXISTING; + unsigned long flags = FILE_ATTRIBUTE_NORMAL; + void* security_descriptor = NULL; + + POLPARAMS_BEGIN(params) + POLPARAM(filename) // Argument 0 + POLPARAM(creation_mode) // Argument 1 + POLPARAM(flags) // Argument 2 + POLPARAM(security_descriptor) + POLPARAMS_END; + + PolicyResult result; + PolicyProcessor eval_CreateFile(policy->entry[kNtFakeCreateFile]); + PolicyProcessor eval_OpenFile(policy->entry[kNtFakeOpenFile]); + PolicyProcessor eval_None(policy->entry[kNtFakeNone]); + + result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_None.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"\\\\??\\c:\\Windows\\System32\\calc.exe"; + flags = FILE_ATTRIBUTE_SYSTEM; + result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_None.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + flags += FILE_ATTRIBUTE_READONLY; + result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_None.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(GIVE_FIRST, eval_None.GetAction()); + result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + flags = FILE_ATTRIBUTE_NORMAL; + result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(SIGNAL_ALARM, eval_CreateFile.GetAction()); + result = eval_None.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"c:\\adobe\\ver3.2\\temp"; + result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_None.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(GIVE_CACHED, eval_OpenFile.GetAction()); + + filename = L"c:\\adobe\\ver3.22\\temp"; + result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"\\\\??\\c:\\some path\\other path\\crash reports\\some path"; + creation_mode = CREATE_ALWAYS; + result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(ASK_BROKER, eval_CreateFile.GetAction()); + result = eval_None.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + + filename = L"\\\\??\\Pipe\\Chrome.12345"; + creation_mode = OPEN_EXISTING; + result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(FAKE_SUCCESS, eval_CreateFile.GetAction()); + result = eval_None.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(NO_POLICY_MATCH, result); + result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params)); + EXPECT_EQ(POLICY_MATCH, result); + EXPECT_EQ(FAKE_SUCCESS, eval_OpenFile.GetAction()); + + delete [] reinterpret_cast(policy); +} + +} // namespace sandbox diff --git a/sandbox/win/src/policy_opcodes_unittest.cc b/sandbox/win/src/policy_opcodes_unittest.cc new file mode 100644 index 0000000..37ccd80 --- /dev/null +++ b/sandbox/win/src/policy_opcodes_unittest.cc @@ -0,0 +1,344 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/sandbox_nt_types.h" +#include "sandbox/src/policy_engine_params.h" +#include "sandbox/src/policy_engine_opcodes.h" +#include "testing/gtest/include/gtest/gtest.h" + + +#define INIT_GLOBAL_RTL(member) \ + g_nt.##member = reinterpret_cast<##member##Function>( \ + ::GetProcAddress(ntdll, #member)); \ + if (NULL == g_nt.##member) \ + return false + +namespace sandbox { + +SANDBOX_INTERCEPT NtExports g_nt; + +bool SetupNtdllImports() { + HMODULE ntdll = ::GetModuleHandle(kNtdllName); + + INIT_GLOBAL_RTL(RtlAllocateHeap); + INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString); + INIT_GLOBAL_RTL(RtlCompareUnicodeString); + INIT_GLOBAL_RTL(RtlCreateHeap); + INIT_GLOBAL_RTL(RtlDestroyHeap); + INIT_GLOBAL_RTL(RtlFreeHeap); + INIT_GLOBAL_RTL(_strnicmp); + INIT_GLOBAL_RTL(strlen); + INIT_GLOBAL_RTL(wcslen); + + return true; +} + +TEST(PolicyEngineTest, ParameterSetTest) { + void* pv1 = reinterpret_cast(0x477EAA5); + const void* pv2 = reinterpret_cast(0x987654); + ParameterSet pset1 = ParamPickerMake(pv1); + ParameterSet pset2 = ParamPickerMake(pv2); + + // Test that we can store and retrieve a void pointer: + const void* result1 =0; + unsigned long result2 = 0; + EXPECT_TRUE(pset1.Get(&result1)); + EXPECT_TRUE(pv1 == result1); + EXPECT_FALSE(pset1.Get(&result2)); + EXPECT_TRUE(pset2.Get(&result1)); + EXPECT_TRUE(pv2 == result1); + EXPECT_FALSE(pset2.Get(&result2)); + + // Test that we can store and retrieve a ulong: + unsigned long number = 12747; + ParameterSet pset3 = ParamPickerMake(number); + EXPECT_FALSE(pset3.Get(&result1)); + EXPECT_TRUE(pset3.Get(&result2)); + EXPECT_EQ(number, result2); + + // Test that we can store and retrieve a string: + const wchar_t* txt = L"S231L"; + ParameterSet pset4 = ParamPickerMake(txt); + const wchar_t* result3 = NULL; + EXPECT_TRUE(pset4.Get(&result3)); + EXPECT_EQ(0, wcscmp(txt, result3)); +} + +TEST(PolicyEngineTest, OpcodeConstraints) { + // Test that PolicyOpcode has no virtual functions + // because these objects are copied over to other processes + // so they cannot have vtables. + EXPECT_FALSE(__is_polymorphic(PolicyOpcode)); + // Keep developers from adding smarts to the opcodes which should + // be pretty much a bag of bytes with a OO interface. + EXPECT_TRUE(__has_trivial_destructor(PolicyOpcode)); + EXPECT_TRUE(__has_trivial_constructor(PolicyOpcode)); + EXPECT_TRUE(__has_trivial_copy(PolicyOpcode)); +} + +TEST(PolicyEngineTest, TrueFalseOpcodes) { + void* dummy = NULL; + ParameterSet ppb1 = ParamPickerMake(dummy); + char memory[1024]; + OpcodeFactory opcode_maker(memory, sizeof(memory)); + + // This opcode always evaluates to true. + PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone); + EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&ppb1, 1, NULL)); + EXPECT_FALSE(op1->IsAction()); + + // This opcode always evaluates to false. + PolicyOpcode* op2 = opcode_maker.MakeOpAlwaysTrue(kPolNone); + EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL)); + + // Nulls not allowed on the params. + EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 0, NULL)); + EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 1, NULL)); + + // True and False opcodes do not 'require' a number of parameters + EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 0, NULL)); + EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL)); + + // Test Inverting the logic. Note that inversion is done outside + // any particular opcode evaluation so no need to repeat for all + // opcodes. + PolicyOpcode* op3 = opcode_maker.MakeOpAlwaysFalse(kPolNegateEval); + EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&ppb1, 1, NULL)); + PolicyOpcode* op4 = opcode_maker.MakeOpAlwaysTrue(kPolNegateEval); + EXPECT_EQ(EVAL_FALSE, op4->Evaluate(&ppb1, 1, NULL)); + + // Test that we clear the match context + PolicyOpcode* op5 = opcode_maker.MakeOpAlwaysTrue(kPolClearContext); + MatchContext context; + context.position = 1; + context.options = kPolUseOREval; + EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&ppb1, 1, &context)); + EXPECT_EQ(0, context.position); + MatchContext context2; + EXPECT_EQ(context2.options, context.options); +} + +TEST(PolicyEngineTest, OpcodeMakerCase1) { + // Testing that the opcode maker does not overrun the + // supplied buffer. It should only be able to make 'count' opcodes. + void* dummy = NULL; + ParameterSet ppb1 = ParamPickerMake(dummy); + + char memory[256]; + OpcodeFactory opcode_maker(memory, sizeof(memory)); + size_t count = sizeof(memory) / sizeof(PolicyOpcode); + + for (size_t ix =0; ix != count; ++ix) { + PolicyOpcode* op = opcode_maker.MakeOpAlwaysFalse(kPolNone); + ASSERT_TRUE(NULL != op); + EXPECT_EQ(EVAL_FALSE, op->Evaluate(&ppb1, 1, NULL)); + } + // There should be no room more another opcode: + PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone); + ASSERT_TRUE(NULL == op1); +} + +TEST(PolicyEngineTest, OpcodeMakerCase2) { + SetupNtdllImports(); + // Testing that the opcode maker does not overrun the + // supplied buffer. It should only be able to make 'count' opcodes. + // The difference with the previous test is that this opcodes allocate + // the string 'txt2' inside the same buffer. + const wchar_t* txt1 = L"1234"; + const wchar_t txt2[] = L"123"; + + ParameterSet ppb1 = ParamPickerMake(txt1); + MatchContext mc1; + + char memory[256]; + OpcodeFactory opcode_maker(memory, sizeof(memory)); + size_t count = sizeof(memory) / (sizeof(PolicyOpcode) + sizeof(txt2)); + + // Test that it does not overrun the buffer. + for (size_t ix =0; ix != count; ++ix) { + PolicyOpcode* op = opcode_maker.MakeOpWStringMatch(0, txt2, 0, + CASE_SENSITIVE, + kPolClearContext); + ASSERT_TRUE(NULL != op); + EXPECT_EQ(EVAL_TRUE, op->Evaluate(&ppb1, 1, &mc1)); + } + + // There should be no room more another opcode: + PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0, + CASE_SENSITIVE, + kPolNone); + ASSERT_TRUE(NULL == op1); +} + +TEST(PolicyEngineTest, IntegerOpcodes) { + const wchar_t* txt = L"abcdef"; + unsigned long num1 = 42; + unsigned long num2 = 113377; + + ParameterSet pp_wrong1 = ParamPickerMake(txt); + ParameterSet pp_num1 = ParamPickerMake(num1); + ParameterSet pp_num2 = ParamPickerMake(num2); + + char memory[128]; + OpcodeFactory opcode_maker(memory, sizeof(memory)); + + // Test basic match for unsigned longs 42 == 42 and 42 != 113377. + PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, unsigned long(42), + kPolNone); + EXPECT_EQ(EVAL_TRUE, op_m42->Evaluate(&pp_num1, 1, NULL)); + EXPECT_EQ(EVAL_FALSE, op_m42->Evaluate(&pp_num2, 1, NULL)); + EXPECT_EQ(EVAL_ERROR, op_m42->Evaluate(&pp_wrong1, 1, NULL)); + + // Test basic match for void pointers. + const void* vp = NULL; + ParameterSet pp_num3 = ParamPickerMake(vp); + PolicyOpcode* op_vp_null = opcode_maker.MakeOpVoidPtrMatch(0, NULL, + kPolNone); + EXPECT_EQ(EVAL_TRUE, op_vp_null->Evaluate(&pp_num3, 1, NULL)); + EXPECT_EQ(EVAL_FALSE, op_vp_null->Evaluate(&pp_num1, 1, NULL)); + EXPECT_EQ(EVAL_ERROR, op_vp_null->Evaluate(&pp_wrong1, 1, NULL)); + + // Basic range test [41 43] (inclusive). + PolicyOpcode* op_range1 = opcode_maker.MakeOpUlongMatchRange(0, 41, 43, + kPolNone); + EXPECT_EQ(EVAL_TRUE, op_range1->Evaluate(&pp_num1, 1, NULL)); + EXPECT_EQ(EVAL_FALSE, op_range1->Evaluate(&pp_num2, 1, NULL)); + EXPECT_EQ(EVAL_ERROR, op_range1->Evaluate(&pp_wrong1, 1, NULL)); +} + +TEST(PolicyEngineTest, LogicalOpcodes) { + char memory[128]; + OpcodeFactory opcode_maker(memory, sizeof(memory)); + + unsigned long num1 = 0x10100702; + ParameterSet pp_num1 = ParamPickerMake(num1); + + PolicyOpcode* op_and1 = opcode_maker.MakeOpUlongAndMatch(0, 0x00100000, + kPolNone); + EXPECT_EQ(EVAL_TRUE, op_and1->Evaluate(&pp_num1, 1, NULL)); + PolicyOpcode* op_and2 = opcode_maker.MakeOpUlongAndMatch(0, 0x00000001, + kPolNone); + EXPECT_EQ(EVAL_FALSE, op_and2->Evaluate(&pp_num1, 1, NULL)); +} + +TEST(PolicyEngineTest, WCharOpcodes1) { + SetupNtdllImports(); + + const wchar_t* txt1 = L"the quick fox jumps over the lazy dog"; + const wchar_t txt2[] = L"the quick"; + const wchar_t txt3[] = L" fox jumps"; + const wchar_t txt4[] = L"the lazy dog"; + const wchar_t txt5[] = L"jumps over"; + const wchar_t txt6[] = L"g"; + + ParameterSet pp_tc1 = ParamPickerMake(txt1); + char memory[512]; + OpcodeFactory opcode_maker(memory, sizeof(memory)); + + PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0, + CASE_SENSITIVE, + kPolNone); + + // Simplest substring match from pos 0. It should be a successful match + // and the match context should be updated. + MatchContext mc1; + EXPECT_EQ(EVAL_TRUE, op1->Evaluate(&pp_tc1, 1, &mc1)); + EXPECT_TRUE(_countof(txt2) == mc1.position + 1); + + // Matching again should fail and the context should be unmodified. + EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&pp_tc1, 1, &mc1)); + EXPECT_TRUE(_countof(txt2) == mc1.position + 1); + + // Using the same match context we should continue where we left + // in the previous successful match, + PolicyOpcode* op3 = opcode_maker.MakeOpWStringMatch(0, txt3, 0, + CASE_SENSITIVE, + kPolNone); + EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&pp_tc1, 1, &mc1)); + EXPECT_TRUE(_countof(txt3) + _countof(txt2) == mc1.position + 2); + + // We now keep on matching but now we skip 6 characters which means + // we skip the string ' over '. And we zero the match context. This is + // the primitive that we use to build '??'. + PolicyOpcode* op4 = opcode_maker.MakeOpWStringMatch(0, txt4, 6, + CASE_SENSITIVE, + kPolClearContext); + EXPECT_EQ(EVAL_TRUE, op4->Evaluate(&pp_tc1, 1, &mc1)); + EXPECT_EQ(0, mc1.position); + + // Test that we can properly match the last part of the string + PolicyOpcode* op4b = opcode_maker.MakeOpWStringMatch(0, txt4, kSeekToEnd, + CASE_SENSITIVE, + kPolClearContext); + EXPECT_EQ(EVAL_TRUE, op4b->Evaluate(&pp_tc1, 1, &mc1)); + EXPECT_EQ(0, mc1.position); + + // Test matching 'jumps over' over the entire string. This is the + // primitive we build '*' from. + PolicyOpcode* op5 = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekForward, + CASE_SENSITIVE, kPolNone); + EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&pp_tc1, 1, &mc1)); + EXPECT_EQ(24, mc1.position); + + // Test that we don't match because it is not at the end of the string + PolicyOpcode* op5b = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekToEnd, + CASE_SENSITIVE, + kPolNone); + EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1)); + + // Test that we function if the string does not fit. In this case we + // try to match 'the lazy dog' against 'he lazy dog'. + PolicyOpcode* op6 = opcode_maker.MakeOpWStringMatch(0, txt4, 2, + CASE_SENSITIVE, kPolNone); + EXPECT_EQ(24, mc1.position); + + // Testing matching against 'g' which should be the last char. + MatchContext mc2; + PolicyOpcode* op7 = opcode_maker.MakeOpWStringMatch(0, txt6, kSeekForward, + CASE_SENSITIVE, kPolNone); + EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2)); + + // Trying to match again should fail since we are in the last char. + // This also covers a couple of boundary conditions. + EXPECT_EQ(EVAL_FALSE, op7->Evaluate(&pp_tc1, 1, &mc2)); +} + +TEST(PolicyEngineTest, WCharOpcodes2) { + SetupNtdllImports(); + + const wchar_t* path1 = L"c:\\documents and settings\\Microsoft\\BLAH.txt"; + const wchar_t txt1[] = L"Settings\\microsoft"; + ParameterSet pp_tc1 = ParamPickerMake(path1); + + char memory[256]; + OpcodeFactory opcode_maker(memory, sizeof(memory)); + MatchContext mc1; + + // Testing case-insensitive does not buy us much since it this option + // is just passed to the Microsoft API that we use normally, but just for + // coverage, here it is: + PolicyOpcode* op1s = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward, + CASE_SENSITIVE, kPolNone); + PolicyOpcode* op1i = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward, + CASE_INSENSITIVE, + kPolNone); + EXPECT_EQ(EVAL_FALSE, op1s->Evaluate(&pp_tc1, 1, &mc1)); + EXPECT_EQ(EVAL_TRUE, op1i->Evaluate(&pp_tc1, 1, &mc1)); + EXPECT_EQ(35, mc1.position); +} + +TEST(PolicyEngineTest, ActionOpcodes) { + char memory[256]; + OpcodeFactory opcode_maker(memory, sizeof(memory)); + MatchContext mc1; + void* dummy = NULL; + ParameterSet ppb1 = ParamPickerMake(dummy); + + PolicyOpcode* op1 = opcode_maker.MakeOpAction(ASK_BROKER, kPolNone); + EXPECT_TRUE(op1->IsAction()); + EXPECT_EQ(ASK_BROKER, op1->Evaluate(&ppb1, 1, &mc1)); +} + +} // namespace sandbox diff --git a/sandbox/win/src/policy_params.h b/sandbox/win/src/policy_params.h new file mode 100644 index 0000000..e1fb3fc --- /dev/null +++ b/sandbox/win/src/policy_params.h @@ -0,0 +1,64 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_POLICY_PARAMS_H__ +#define SANDBOX_SRC_POLICY_PARAMS_H__ + +#include "sandbox/src/policy_engine_params.h" + +namespace sandbox { + +class ParameterSet; + +// Warning: The following macros store the address to the actual variables, in +// other words, the values are not copied. +#define POLPARAMS_BEGIN(type) class type { public: enum Args { +#define POLPARAM(arg) arg, +#define POLPARAMS_END(type) PolParamLast }; }; \ + typedef sandbox::ParameterSet type##Array [type::PolParamLast]; + +// Policy parameters for file open / create. +POLPARAMS_BEGIN(OpenFile) + POLPARAM(NAME) + POLPARAM(BROKER) // TRUE if called from the broker. + POLPARAM(ACCESS) + POLPARAM(OPTIONS) +POLPARAMS_END(OpenFile) + +// Policy parameter for name-based policies. +POLPARAMS_BEGIN(FileName) + POLPARAM(NAME) + POLPARAM(BROKER) // TRUE if called from the broker. +POLPARAMS_END(FileName) + +COMPILE_ASSERT(OpenFile::NAME == FileName::NAME, to_simplify_fs_policies); +COMPILE_ASSERT(OpenFile::BROKER == FileName::BROKER, to_simplify_fs_policies); + +// Policy parameter for name-based policies. +POLPARAMS_BEGIN(NameBased) + POLPARAM(NAME) +POLPARAMS_END(NameBased) + +// Policy parameters for open event. +POLPARAMS_BEGIN(OpenEventParams) + POLPARAM(NAME) + POLPARAM(ACCESS) +POLPARAMS_END(OpenEventParams) + +// Policy Parameters for reg open / create. +POLPARAMS_BEGIN(OpenKey) + POLPARAM(NAME) + POLPARAM(ACCESS) +POLPARAMS_END(OpenKey) + +// Policy parameter for name-based policies. +POLPARAMS_BEGIN(HandleTarget) + POLPARAM(NAME) + POLPARAM(TARGET) +POLPARAMS_END(HandleTarget) + + +} // namespace sandbox + +#endif // SANDBOX_SRC_POLICY_PARAMS_H__ diff --git a/sandbox/win/src/policy_target.cc b/sandbox/win/src/policy_target.cc new file mode 100644 index 0000000..aa69892 --- /dev/null +++ b/sandbox/win/src/policy_target.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/policy_target.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_engine_processor.h" +#include "sandbox/src/policy_low_level.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +// Handle for our private heap. +extern void* g_heap; + +// This is the list of all imported symbols from ntdll.dll. +SANDBOX_INTERCEPT NtExports g_nt; + +// Policy data. +extern void* volatile g_shared_policy_memory; +SANDBOX_INTERCEPT size_t g_shared_policy_size; + +bool QueryBroker(int ipc_id, CountedParameterSetBase* params) { + DCHECK_NT(static_cast(ipc_id) < kMaxServiceCount); + DCHECK_NT(g_shared_policy_memory); + DCHECK_NT(g_shared_policy_size > 0); + + if (static_cast(ipc_id) >= kMaxServiceCount) + return false; + + PolicyGlobal* global_policy = + reinterpret_cast(g_shared_policy_memory); + + if (!global_policy->entry[ipc_id]) + return false; + + PolicyBuffer* policy = reinterpret_cast( + reinterpret_cast(g_shared_policy_memory) + + reinterpret_cast(global_policy->entry[ipc_id])); + + if ((reinterpret_cast(global_policy->entry[ipc_id]) > + global_policy->data_size) || + (g_shared_policy_size < global_policy->data_size)) { + NOTREACHED_NT(); + return false; + } + + for (int i = 0; i < params->count; i++) { + if (!params->parameters[i].IsValid()) { + NOTREACHED_NT(); + return false; + } + } + + PolicyProcessor processor(policy); + PolicyResult result = processor.Evaluate(kShortEval, params->parameters, + params->count); + DCHECK_NT(POLICY_ERROR != result); + + return POLICY_MATCH == result && ASK_BROKER == processor.GetAction(); +} + +// ----------------------------------------------------------------------- + +// Hooks NtSetInformationThread to block RevertToSelf from being +// called before the actual call to LowerToken. +NTSTATUS WINAPI TargetNtSetInformationThread( + NtSetInformationThreadFunction orig_SetInformationThread, HANDLE thread, + NT_THREAD_INFORMATION_CLASS thread_info_class, PVOID thread_information, + ULONG thread_information_bytes) { + do { + if (SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) + break; + if (ThreadImpersonationToken != thread_info_class) + break; + if (!thread_information) + break; + HANDLE token; + if (sizeof(token) > thread_information_bytes) + break; + + NTSTATUS ret = CopyData(&token, thread_information, sizeof(token)); + if (!NT_SUCCESS(ret) || NULL != token) + break; + + // This is a revert to self. + return STATUS_SUCCESS; + } while (false); + + return orig_SetInformationThread(thread, thread_info_class, + thread_information, + thread_information_bytes); +} + +// Hooks NtOpenThreadToken to force the open_as_self parameter to be set to +// FALSE if we are still running with the impersonation token. open_as_self set +// to TRUE means that the token will be open using the process token instead of +// the impersonation token. This is bad because the process token does not have +// access to open the thread token. +NTSTATUS WINAPI TargetNtOpenThreadToken( + NtOpenThreadTokenFunction orig_OpenThreadToken, HANDLE thread, + ACCESS_MASK desired_access, BOOLEAN open_as_self, PHANDLE token) { + if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) + open_as_self = FALSE; + + return orig_OpenThreadToken(thread, desired_access, open_as_self, token); +} + +// See comment for TargetNtOpenThreadToken +NTSTATUS WINAPI TargetNtOpenThreadTokenEx( + NtOpenThreadTokenExFunction orig_OpenThreadTokenEx, HANDLE thread, + ACCESS_MASK desired_access, BOOLEAN open_as_self, ULONG handle_attributes, + PHANDLE token) { + if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) + open_as_self = FALSE; + + return orig_OpenThreadTokenEx(thread, desired_access, open_as_self, + handle_attributes, token); +} + +} // namespace sandbox diff --git a/sandbox/win/src/policy_target.h b/sandbox/win/src/policy_target.h new file mode 100644 index 0000000..4a77b47 --- /dev/null +++ b/sandbox/win/src/policy_target.h @@ -0,0 +1,45 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +#ifndef SANDBOX_SRC_POLICY_TARGET_H__ +#define SANDBOX_SRC_POLICY_TARGET_H__ + +namespace sandbox { + +struct CountedParameterSetBase; + +// Performs a policy lookup and returns true if the request should be passed to +// the broker process. +bool QueryBroker(int ipc_id, CountedParameterSetBase* params); + +extern "C" { + +// Interception of NtSetInformationThread on the child process. +// It should never be called directly. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationThread( + NtSetInformationThreadFunction orig_SetInformationThread, HANDLE thread, + NT_THREAD_INFORMATION_CLASS thread_info_class, PVOID thread_information, + ULONG thread_information_bytes); + +// Interception of NtOpenThreadToken on the child process. +// It should never be called directly +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadToken( + NtOpenThreadTokenFunction orig_OpenThreadToken, HANDLE thread, + ACCESS_MASK desired_access, BOOLEAN open_as_self, PHANDLE token); + +// Interception of NtOpenThreadTokenEx on the child process. +// It should never be called directly +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadTokenEx( + NtOpenThreadTokenExFunction orig_OpenThreadTokenEx, HANDLE thread, + ACCESS_MASK desired_access, BOOLEAN open_as_self, ULONG handle_attributes, + PHANDLE token); + +} // extern "C" + +} // namespace sandbox + +#endif // SANDBOX_SRC_POLICY_TARGET_H__ diff --git a/sandbox/win/src/policy_target_test.cc b/sandbox/win/src/policy_target_test.cc new file mode 100644 index 0000000..aef7548 --- /dev/null +++ b/sandbox/win/src/policy_target_test.cc @@ -0,0 +1,337 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/win/scoped_process_information.h" +#include "base/win/windows_version.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_utils.h" +#include "sandbox/src/target_services.h" +#include "sandbox/tests/common/controller.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +#define BINDNTDLL(name) \ + name ## Function name = reinterpret_cast( \ + ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) + +// Reverts to self and verify that SetInformationToken was faked. Returns +// SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked. +SBOX_TESTS_COMMAND int PolicyTargetTest_token(int argc, wchar_t **argv) { + HANDLE thread_token; + // Get the thread token, using impersonation. + if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | + TOKEN_DUPLICATE, FALSE, &thread_token)) + return ::GetLastError(); + + ::RevertToSelf(); + ::CloseHandle(thread_token); + + int ret = SBOX_TEST_FAILED; + if (::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, + FALSE, &thread_token)) { + ret = SBOX_TEST_SUCCEEDED; + ::CloseHandle(thread_token); + } + return ret; +} + +// Stores the high privilege token on a static variable, change impersonation +// again to that one and verify that we are not interfering anymore with +// RevertToSelf. +SBOX_TESTS_COMMAND int PolicyTargetTest_steal(int argc, wchar_t **argv) { + static HANDLE thread_token; + if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) { + if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | + TOKEN_DUPLICATE, FALSE, &thread_token)) + return ::GetLastError(); + } else { + if (!::SetThreadToken(NULL, thread_token)) + return ::GetLastError(); + + // See if we fake the call again. + int ret = PolicyTargetTest_token(argc, argv); + ::CloseHandle(thread_token); + return ret; + } + return 0; +} + +// Opens the thread token with and without impersonation. +SBOX_TESTS_COMMAND int PolicyTargetTest_token2(int argc, wchar_t **argv) { + HANDLE thread_token; + // Get the thread token, using impersonation. + if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | + TOKEN_DUPLICATE, FALSE, &thread_token)) + return ::GetLastError(); + ::CloseHandle(thread_token); + + // Get the thread token, without impersonation. + if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, + TRUE, &thread_token)) + return ::GetLastError(); + ::CloseHandle(thread_token); + return SBOX_TEST_SUCCEEDED; +} + +// Opens the thread token with and without impersonation, using +// NtOpenThreadTokenEX. +SBOX_TESTS_COMMAND int PolicyTargetTest_token3(int argc, wchar_t **argv) { + BINDNTDLL(NtOpenThreadTokenEx); + if (!NtOpenThreadTokenEx) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + HANDLE thread_token; + // Get the thread token, using impersonation. + NTSTATUS status = NtOpenThreadTokenEx(GetCurrentThread(), + TOKEN_IMPERSONATE | TOKEN_DUPLICATE, + FALSE, 0, &thread_token); + if (status == STATUS_NO_TOKEN) + return ERROR_NO_TOKEN; + if (!NT_SUCCESS(status)) + return SBOX_TEST_FAILED; + + ::CloseHandle(thread_token); + + // Get the thread token, without impersonation. + status = NtOpenThreadTokenEx(GetCurrentThread(), + TOKEN_IMPERSONATE | TOKEN_DUPLICATE, TRUE, 0, + &thread_token); + if (!NT_SUCCESS(status)) + return SBOX_TEST_FAILED; + + ::CloseHandle(thread_token); + return SBOX_TEST_SUCCEEDED; +} + +// Tests that we can open the current thread. +SBOX_TESTS_COMMAND int PolicyTargetTest_thread(int argc, wchar_t **argv) { + DWORD thread_id = ::GetCurrentThreadId(); + HANDLE thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id); + if (!thread) + return ::GetLastError(); + if (!::CloseHandle(thread)) + return ::GetLastError(); + + return SBOX_TEST_SUCCEEDED; +} + +// New thread entry point: do nothing. +DWORD WINAPI PolicyTargetTest_thread_main(void* param) { + ::Sleep(INFINITE); + return 0; +} + +// Tests that we can create a new thread, and open it. +SBOX_TESTS_COMMAND int PolicyTargetTest_thread2(int argc, wchar_t **argv) { + // Use default values to create a new thread. + DWORD thread_id; + HANDLE thread = ::CreateThread(NULL, 0, &PolicyTargetTest_thread_main, 0, 0, + &thread_id); + if (!thread) + return ::GetLastError(); + if (!::CloseHandle(thread)) + return ::GetLastError(); + + thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id); + if (!thread) + return ::GetLastError(); + + if (!::CloseHandle(thread)) + return ::GetLastError(); + + return SBOX_TEST_SUCCEEDED; +} + +// Tests that we can call CreateProcess. +SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t **argv) { + // Use default values to create a new process. + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + base::win::ScopedProcessInformation process_info; + if (!::CreateProcessW(L"foo.exe", L"foo.exe", NULL, NULL, FALSE, 0, + NULL, NULL, &startup_info, process_info.Receive())) + return SBOX_TEST_SUCCEEDED; + return SBOX_TEST_FAILED; +} + +TEST(PolicyTargetTest, SetInformationThread) { + TestRunner runner; + if (base::win::GetVersion() >= base::win::VERSION_XP) { + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token")); + } + + runner.SetTestState(AFTER_REVERT); + EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token")); + + runner.SetTestState(EVERY_STATE); + if (base::win::GetVersion() >= base::win::VERSION_XP) + EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal")); +} + +TEST(PolicyTargetTest, OpenThreadToken) { + TestRunner runner; + if (base::win::GetVersion() >= base::win::VERSION_XP) { + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2")); + } + + runner.SetTestState(AFTER_REVERT); + EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2")); +} + +TEST(PolicyTargetTest, OpenThreadTokenEx) { + TestRunner runner; + if (base::win::GetVersion() < base::win::VERSION_XP) + return; + + runner.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token3")); + + runner.SetTestState(AFTER_REVERT); + EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token3")); +} + +TEST(PolicyTargetTest, OpenThread) { + TestRunner runner; + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread")) << + "Opens the current thread"; + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread2")) << + "Creates a new thread and opens it"; +} + +TEST(PolicyTargetTest, OpenProcess) { + TestRunner runner; + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_process")) << + "Opens a process"; +} + +// Launches the app in the sandbox and ask it to wait in an +// infinite loop. Waits for 2 seconds and then check if the +// desktop associated with the app thread is not the same as the +// current desktop. +TEST(PolicyTargetTest, DesktopPolicy) { + BrokerServices* broker = GetBroker(); + + // Precreate the desktop. + TargetPolicy* temp_policy = broker->CreatePolicy(); + temp_policy->CreateAlternateDesktop(false); + temp_policy->Release(); + + ASSERT_TRUE(broker != NULL); + + // Get the path to the sandboxed app. + wchar_t prog_name[MAX_PATH]; + GetModuleFileNameW(NULL, prog_name, MAX_PATH); + + std::wstring arguments(L"\""); + arguments += prog_name; + arguments += L"\" -child 0 wait"; // Don't care about the "state" argument. + + // Launch the app. + ResultCode result = SBOX_ALL_OK; + base::win::ScopedProcessInformation target; + + TargetPolicy* policy = broker->CreatePolicy(); + policy->SetAlternateDesktop(false); + policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); + result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, + target.Receive()); + policy->Release(); + + EXPECT_EQ(SBOX_ALL_OK, result); + + EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); + + EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000)); + + EXPECT_NE(::GetThreadDesktop(target.thread_id()), + ::GetThreadDesktop(::GetCurrentThreadId())); + + std::wstring desktop_name = policy->GetAlternateDesktop(); + HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); + EXPECT_TRUE(NULL != desk); + EXPECT_TRUE(::CloseDesktop(desk)); + EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); + + ::WaitForSingleObject(target.process_handle(), INFINITE); + + // Close the desktop handle. + temp_policy = broker->CreatePolicy(); + temp_policy->DestroyAlternateDesktop(); + temp_policy->Release(); + + // Make sure the desktop does not exist anymore. + desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); + EXPECT_TRUE(NULL == desk); +} + +// Launches the app in the sandbox and ask it to wait in an +// infinite loop. Waits for 2 seconds and then check if the +// winstation associated with the app thread is not the same as the +// current desktop. +TEST(PolicyTargetTest, WinstaPolicy) { + BrokerServices* broker = GetBroker(); + + // Precreate the desktop. + TargetPolicy* temp_policy = broker->CreatePolicy(); + temp_policy->CreateAlternateDesktop(true); + temp_policy->Release(); + + ASSERT_TRUE(broker != NULL); + + // Get the path to the sandboxed app. + wchar_t prog_name[MAX_PATH]; + GetModuleFileNameW(NULL, prog_name, MAX_PATH); + + std::wstring arguments(L"\""); + arguments += prog_name; + arguments += L"\" -child 0 wait"; // Don't care about the "state" argument. + + // Launch the app. + ResultCode result = SBOX_ALL_OK; + base::win::ScopedProcessInformation target; + + TargetPolicy* policy = broker->CreatePolicy(); + policy->SetAlternateDesktop(true); + policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); + result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, + target.Receive()); + policy->Release(); + + EXPECT_EQ(SBOX_ALL_OK, result); + + EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); + + EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000)); + + EXPECT_NE(::GetThreadDesktop(target.thread_id()), + ::GetThreadDesktop(::GetCurrentThreadId())); + + std::wstring desktop_name = policy->GetAlternateDesktop(); + ASSERT_FALSE(desktop_name.empty()); + + // Make sure there is a backslash, for the window station name. + EXPECT_NE(desktop_name.find_first_of(L'\\'), std::wstring::npos); + + // Isolate the desktop name. + desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1); + + HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); + // This should fail if the desktop is really on another window station. + EXPECT_FALSE(NULL != desk); + EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); + + ::WaitForSingleObject(target.process_handle(), INFINITE); + + // Close the desktop handle. + temp_policy = broker->CreatePolicy(); + temp_policy->DestroyAlternateDesktop(); + temp_policy->Release(); +} + +} // namespace sandbox diff --git a/sandbox/win/src/process_policy_test.cc b/sandbox/win/src/process_policy_test.cc new file mode 100644 index 0000000..783446e --- /dev/null +++ b/sandbox/win/src/process_policy_test.cc @@ -0,0 +1,295 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "base/sys_string_conversions.h" +#include "base/win/scoped_handle.h" +#include "base/win/scoped_process_information.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/tests/common/controller.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// While the shell API provides better calls than this home brew function +// we use GetSystemWindowsDirectoryW which does not query the registry so +// it is safe to use after revert. +std::wstring MakeFullPathToSystem32(const wchar_t* name) { + wchar_t windows_path[MAX_PATH] = {0}; + ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH); + std::wstring full_path(windows_path); + if (full_path.empty()) { + return full_path; + } + full_path += L"\\system32\\"; + full_path += name; + return full_path; +} + +// Creates a process with the |exe| and |command| parameter using the +// unicode and ascii version of the api. +sandbox::SboxTestResult CreateProcessHelper(const std::wstring &exe, + const std::wstring &command) { + base::win::ScopedProcessInformation pi; + STARTUPINFOW si = {sizeof(si)}; + + const wchar_t *exe_name = NULL; + if (!exe.empty()) + exe_name = exe.c_str(); + + const wchar_t *cmd_line = NULL; + if (!command.empty()) + cmd_line = command.c_str(); + + // Create the process with the unicode version of the API. + sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED; + if (!::CreateProcessW(exe_name, const_cast(cmd_line), NULL, NULL, + FALSE, 0, NULL, NULL, &si, pi.Receive())) { + DWORD last_error = GetLastError(); + if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || + (ERROR_ACCESS_DENIED == last_error) || + (ERROR_FILE_NOT_FOUND == last_error)) { + ret1 = sandbox::SBOX_TEST_DENIED; + } else { + ret1 = sandbox::SBOX_TEST_FAILED; + } + } else { + ret1 = sandbox::SBOX_TEST_SUCCEEDED; + } + + pi.Close(); + + // Do the same with the ansi version of the api + STARTUPINFOA sia = {sizeof(sia)}; + sandbox::SboxTestResult ret2 = sandbox::SBOX_TEST_FAILED; + + std::string narrow_cmd_line; + if (cmd_line) + narrow_cmd_line = base::SysWideToMultiByte(cmd_line, CP_UTF8); + if (!::CreateProcessA( + exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str() : NULL, + cmd_line ? const_cast(narrow_cmd_line.c_str()) : NULL, + NULL, NULL, FALSE, 0, NULL, NULL, &sia, pi.Receive())) { + DWORD last_error = GetLastError(); + if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || + (ERROR_ACCESS_DENIED == last_error) || + (ERROR_FILE_NOT_FOUND == last_error)) { + ret2 = sandbox::SBOX_TEST_DENIED; + } else { + ret2 = sandbox::SBOX_TEST_FAILED; + } + } else { + ret2 = sandbox::SBOX_TEST_SUCCEEDED; + } + + if (ret1 == ret2) + return ret1; + + return sandbox::SBOX_TEST_FAILED; +} + +} // namespace + +namespace sandbox { + +// Tries to create the process in argv[0] using 7 different ways. +// Since we also try the Ansi and Unicode version of the CreateProcess API, +// The process referenced by argv[0] will be spawned 14 times. +SBOX_TESTS_COMMAND int Process_RunApp(int argc, wchar_t **argv) { + if (argc != 1) { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + if ((NULL == argv) || (NULL == argv[0])) { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + std::wstring path = MakeFullPathToSystem32(argv[0]); + + // TEST 1: Try with the path in the app_name. + int result1 = CreateProcessHelper(path, std::wstring()); + + // TEST 2: Try with the path in the cmd_line. + std::wstring cmd_line = L"\""; + cmd_line += path; + cmd_line += L"\""; + int result2 = CreateProcessHelper(std::wstring(), cmd_line); + + // TEST 3: Try file name in the cmd_line. + int result3 = CreateProcessHelper(std::wstring(), argv[0]); + + // TEST 4: Try file name in the app_name and current directory sets correctly. + std::wstring system32 = MakeFullPathToSystem32(L""); + wchar_t current_directory[MAX_PATH + 1]; + int result4; + bool test_succeeded = false; + DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory); + if (0 != ret && ret < MAX_PATH) { + current_directory[ret] = L'\\'; + current_directory[ret+1] = L'\0'; + if (::SetCurrentDirectory(system32.c_str())) { + result4 = CreateProcessHelper(argv[0], std::wstring()); + if (::SetCurrentDirectory(current_directory)) { + test_succeeded = true; + } + } + } + if (!test_succeeded) + result4 = SBOX_TEST_FAILED; + + // TEST 5: Try with the path in the cmd_line and arguments. + cmd_line = L"\""; + cmd_line += path; + cmd_line += L"\" /INSERT"; + int result5 = CreateProcessHelper(std::wstring(), cmd_line); + + // TEST 6: Try with the file_name in the cmd_line and arguments. + cmd_line = argv[0]; + cmd_line += L" /INSERT"; + int result6 = CreateProcessHelper(std::wstring(), cmd_line); + + // TEST 7: Try with the path without the drive. + cmd_line = path.substr(path.find(L'\\')); + int result7 = CreateProcessHelper(std::wstring(), cmd_line); + + // Check if they all returned the same thing. + if ((result1 == result2) && (result2 == result3) && (result3 == result4) && + (result4 == result5) && (result5 == result6) && (result6 == result7)) + return result1; + + return SBOX_TEST_FAILED; +} + +// Creates a process and checks if it's possible to get a handle to it's token. +SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t **argv) { + if (argc != 1) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + if ((NULL == argv) || (NULL == argv[0])) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + std::wstring path = MakeFullPathToSystem32(argv[0]); + + base::win::ScopedProcessInformation pi; + STARTUPINFOW si = {sizeof(si)}; + + if (!::CreateProcessW(path.c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, + NULL, NULL, &si, pi.Receive())) { + return SBOX_TEST_FAILED; + } + + HANDLE token = NULL; + BOOL result = + ::OpenProcessToken(pi.process_handle(), TOKEN_IMPERSONATE, &token); + DWORD error = ::GetLastError(); + + base::win::ScopedHandle token_handle(token); + + if (!::TerminateProcess(pi.process_handle(), 0)) + return SBOX_TEST_FAILED; + + if (result && token) + return SBOX_TEST_SUCCEEDED; + + if (ERROR_ACCESS_DENIED == error) + return SBOX_TEST_DENIED; + + return SBOX_TEST_FAILED; +} + + +SBOX_TESTS_COMMAND int Process_OpenToken(int argc, wchar_t **argv) { + HANDLE token; + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) { + if (ERROR_ACCESS_DENIED == ::GetLastError()) { + return SBOX_TEST_DENIED; + } + } else { + ::CloseHandle(token); + return SBOX_TEST_SUCCEEDED; + } + + return SBOX_TEST_FAILED; +} + +TEST(ProcessPolicyTest, TestAllAccess) { + // Check if the "all access" rule fails to be added when the token is too + // powerful. + TestRunner runner; + + // Check the failing case. + runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); + EXPECT_EQ(SBOX_ERROR_UNSUPPORTED, + runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS, + TargetPolicy::PROCESS_ALL_EXEC, + L"this is not important")); + + // Check the working case. + runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_INTERACTIVE); + + EXPECT_EQ(SBOX_ALL_OK, + runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS, + TargetPolicy::PROCESS_ALL_EXEC, + L"this is not important")); +} + +// This test is disabled. See bug 1305476. +TEST(ProcessPolicyTest, DISABLED_RunFindstrExe) { + TestRunner runner; + std::wstring exe_path = MakeFullPathToSystem32(L"findstr.exe"); + std::wstring system32 = MakeFullPathToSystem32(L""); + ASSERT_TRUE(!exe_path.empty()); + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, + TargetPolicy::PROCESS_MIN_EXEC, + exe_path.c_str())); + + // Need to add directory rules for the directories that we use in + // SetCurrentDirectory. + EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, + system32.c_str())); + + wchar_t current_directory[MAX_PATH]; + DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory); + ASSERT_TRUE(0 != ret && ret < MAX_PATH); + + wcscat_s(current_directory, MAX_PATH, L"\\"); + EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, + current_directory)); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_RunApp findstr.exe")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp calc.exe")); +} + +TEST(ProcessPolicyTest, OpenToken) { + TestRunner runner; + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_OpenToken")); +} + +TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) { + TestRunner runner; + std::wstring exe_path = MakeFullPathToSystem32(L"findstr.exe"); + ASSERT_TRUE(!exe_path.empty()); + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, + TargetPolicy::PROCESS_MIN_EXEC, + exe_path.c_str())); + + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); +} + +TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) { + TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE); + std::wstring exe_path = MakeFullPathToSystem32(L"findstr.exe"); + ASSERT_TRUE(!exe_path.empty()); + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, + TargetPolicy::PROCESS_ALL_EXEC, + exe_path.c_str())); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); +} + +} // namespace sandbox diff --git a/sandbox/win/src/process_thread_dispatcher.cc b/sandbox/win/src/process_thread_dispatcher.cc new file mode 100644 index 0000000..cc137a5 --- /dev/null +++ b/sandbox/win/src/process_thread_dispatcher.cc @@ -0,0 +1,245 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/process_thread_dispatcher.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_broker.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/process_thread_interception.h" +#include "sandbox/src/process_thread_policy.h" +#include "sandbox/src/sandbox.h" + +namespace { + +// Extracts the application name from a command line. +// +// The application name is the first element of the command line. If +// there is no quotes, the first element is delimited by the first space. +// If there are quotes, the first element is delimited by the quotes. +// +// The create process call is smarter than us. It tries really hard to launch +// the process even if the command line is wrong. For example: +// "c:\program files\test param" will first try to launch c:\program.exe then +// c:\program files\test.exe. We don't do that, we stop after at the first +// space when there is no quotes. +std::wstring GetPathFromCmdLine(const std::wstring &cmd_line) { + std::wstring exe_name; + // Check if it starts with '"'. + if (cmd_line[0] == L'\"') { + // Find the position of the second '"', this terminates the path. + std::wstring::size_type pos = cmd_line.find(L'\"', 1); + if (std::wstring::npos == pos) + return cmd_line; + exe_name = cmd_line.substr(1, pos - 1); + } else { + // There is no '"', that means that the appname is terminated at the + // first space. + std::wstring::size_type pos = cmd_line.find(L' '); + if (std::wstring::npos == pos) { + // There is no space, the cmd_line contains only the app_name + exe_name = cmd_line; + } else { + exe_name = cmd_line.substr(0, pos); + } + } + + return exe_name; +} + +// Returns true is the path in parameter is relative. False if it's +// absolute. +bool IsPathRelative(const std::wstring &path) { + // A path is Relative if it's not a UNC path beginnning with \\ or a + // path beginning with a drive. (i.e. X:\) + if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1) + return false; + return true; +} + +// Converts a relative path to an absolute path. +bool ConvertToAbsolutePath(const std::wstring child_current_directory, + bool use_env_path, std::wstring *path) { + wchar_t file_buffer[MAX_PATH]; + wchar_t *file_part = NULL; + + // Here we should start by looking at the path where the child application was + // started. We don't have this information yet. + DWORD result = 0; + if (use_env_path) { + // Try with the complete path + result = ::SearchPath(NULL, path->c_str(), NULL, MAX_PATH, file_buffer, + &file_part); + } + + if (0 == result) { + // Try with the current directory of the child + result = ::SearchPath(child_current_directory.c_str(), path->c_str(), NULL, + MAX_PATH, file_buffer, &file_part); + } + + if (0 == result || result >= MAX_PATH) + return false; + + *path = file_buffer; + return true; +} + +} // namespace +namespace sandbox { + +ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base) + : policy_base_(policy_base) { + static const IPCCall open_thread = { + {IPC_NTOPENTHREAD_TAG, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast( + &ThreadProcessDispatcher::NtOpenThread) + }; + + static const IPCCall open_process = { + {IPC_NTOPENPROCESS_TAG, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast( + &ThreadProcessDispatcher::NtOpenProcess) + }; + + static const IPCCall process_token = { + {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, ULONG_TYPE}, + reinterpret_cast( + &ThreadProcessDispatcher::NtOpenProcessToken) + }; + + static const IPCCall process_tokenex = { + {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast( + &ThreadProcessDispatcher::NtOpenProcessTokenEx) + }; + + static const IPCCall create_params = { + {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE}, + reinterpret_cast( + &ThreadProcessDispatcher::CreateProcessW) + }; + + ipc_calls_.push_back(open_thread); + ipc_calls_.push_back(open_process); + ipc_calls_.push_back(process_token); + ipc_calls_.push_back(process_tokenex); + ipc_calls_.push_back(create_params); +} + +bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager, + int service) { + switch (service) { + case IPC_NTOPENTHREAD_TAG: + case IPC_NTOPENPROCESS_TAG: + case IPC_NTOPENPROCESSTOKEN_TAG: + case IPC_NTOPENPROCESSTOKENEX_TAG: + // There is no explicit policy for these services. + NOTREACHED(); + return false; + + case IPC_CREATEPROCESSW_TAG: + return INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessW, + CREATE_PROCESSW_ID, 44) && + INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA, + CREATE_PROCESSA_ID, 44); + + default: + return false; + } +} + +bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, DWORD desired_access, + DWORD thread_id) { + HANDLE handle; + NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info, + desired_access, thread_id, + &handle); + ipc->return_info.nt_status = ret; + ipc->return_info.handle = handle; + return true; +} + +bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, DWORD desired_access, + DWORD process_id) { + HANDLE handle; + NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info, + desired_access, process_id, + &handle); + ipc->return_info.nt_status = ret; + ipc->return_info.handle = handle; + return true; +} + +bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, HANDLE process, + DWORD desired_access) { + HANDLE handle; + NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info, + process, desired_access, + &handle); + ipc->return_info.nt_status = ret; + ipc->return_info.handle = handle; + return true; +} + +bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, + DWORD desired_access, + DWORD attributes) { + HANDLE handle; + NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info, + process, + desired_access, + attributes, &handle); + ipc->return_info.nt_status = ret; + ipc->return_info.handle = handle; + return true; +} + +bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, std::wstring* name, + std::wstring* cmd_line, + std::wstring* cur_dir, + CountedBuffer* info) { + if (sizeof(PROCESS_INFORMATION) != info->Size()) + return false; + + // Check if there is an application name. + std::wstring exe_name; + if (!name->empty()) + exe_name = *name; + else + exe_name = GetPathFromCmdLine(*cmd_line); + + if (IsPathRelative(exe_name)) { + if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) { + // Cannot find the path. Maybe the file does not exist. + ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND; + return true; + } + } + + const wchar_t* const_exe_name = exe_name.c_str(); + CountedParameterSet params; + params[NameBased::NAME] = ParamPickerMake(const_exe_name); + + EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG, + params.GetBase()); + + PROCESS_INFORMATION* proc_info = + reinterpret_cast(info->Buffer()); + // Here we force the app_name to be the one we used for the policy lookup. + // If our logic was wrong, at least we wont allow create a random process. + DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info, + exe_name, *cmd_line, + proc_info); + + ipc->return_info.win32_result = ret; + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/process_thread_dispatcher.h b/sandbox/win/src/process_thread_dispatcher.h new file mode 100644 index 0000000..45fad03 --- /dev/null +++ b/sandbox/win/src/process_thread_dispatcher.h @@ -0,0 +1,47 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_ +#define SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_ + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sandbox_policy_base.h" + +namespace sandbox { + +// This class handles process and thread-related IPC calls. +class ThreadProcessDispatcher : public Dispatcher { + public: + explicit ThreadProcessDispatcher(PolicyBase* policy_base); + ~ThreadProcessDispatcher() {} + + // Dispatcher interface. + virtual bool SetupService(InterceptionManager* manager, int service); + + private: + // Processes IPC requests coming from calls to NtOpenThread() in the target. + bool NtOpenThread(IPCInfo* ipc, DWORD desired_access, DWORD thread_id); + + // Processes IPC requests coming from calls to NtOpenProcess() in the target. + bool NtOpenProcess(IPCInfo* ipc, DWORD desired_access, DWORD process_id); + + // Processes IPC requests from calls to NtOpenProcessToken() in the target. + bool NtOpenProcessToken(IPCInfo* ipc, HANDLE process, DWORD desired_access); + + // Processes IPC requests from calls to NtOpenProcessTokenEx() in the target. + bool NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, DWORD desired_access, + DWORD attributes); + + // Processes IPC requests coming from calls to CreateProcessW() in the target. + bool CreateProcessW(IPCInfo* ipc, std::wstring* name, std::wstring* cmd_line, + std::wstring* cur_dir, CountedBuffer* info); + + PolicyBase* policy_base_; + DISALLOW_COPY_AND_ASSIGN(ThreadProcessDispatcher); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_ diff --git a/sandbox/win/src/process_thread_interception.cc b/sandbox/win/src/process_thread_interception.cc new file mode 100644 index 0000000..e847908 --- /dev/null +++ b/sandbox/win/src/process_thread_interception.cc @@ -0,0 +1,447 @@ +// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/process_thread_interception.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/policy_target.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +SANDBOX_INTERCEPT NtExports g_nt; + +// Hooks NtOpenThread and proxy the call to the broker if it's trying to +// open a thread in the same process. +NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, + PHANDLE thread, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + PCLIENT_ID client_id) { + NTSTATUS status = orig_OpenThread(thread, desired_access, object_attributes, + client_id); + if (NT_SUCCESS(status)) + return status; + + do { + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + break; + if (!client_id) + break; + + uint32 thread_id = 0; + bool should_break = false; + __try { + // We support only the calls for the current process + if (NULL != client_id->UniqueProcess) + should_break = true; + + // Object attributes should be NULL or empty. + if (!should_break && NULL != object_attributes) { + if (0 != object_attributes->Attributes || + NULL != object_attributes->ObjectName || + NULL != object_attributes->RootDirectory || + NULL != object_attributes->SecurityDescriptor || + NULL != object_attributes->SecurityQualityOfService) { + should_break = true; + } + } + + thread_id = static_cast( + reinterpret_cast(client_id->UniqueThread)); + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + + if (should_break) + break; + + if (!ValidParameter(thread, sizeof(HANDLE), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_NTOPENTHREAD_TAG, desired_access, + thread_id, &answer); + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + // The nt_status here is most likely STATUS_INVALID_CID because + // in the broker we set the process id in the CID (client ID) param + // to be the current process. If you try to open a thread from another + // process you will get this INVALID_CID error. On the other hand, if you + // try to open a thread in your own process, it should return success. + // We don't want to return STATUS_INVALID_CID here, so we return the + // return of the original open thread status, which is most likely + // STATUS_ACCESS_DENIED. + break; + + __try { + // Write the output parameters. + *thread = answer.handle; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + + return answer.nt_status; + } while (false); + + return status; +} + +// Hooks NtOpenProcess and proxy the call to the broker if it's trying to +// open the current process. +NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess, + PHANDLE process, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + PCLIENT_ID client_id) { + NTSTATUS status = orig_OpenProcess(process, desired_access, object_attributes, + client_id); + if (NT_SUCCESS(status)) + return status; + + do { + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + break; + if (!client_id) + break; + + uint32 process_id = 0; + bool should_break = false; + __try { + // Object attributes should be NULL or empty. + if (!should_break && NULL != object_attributes) { + if (0 != object_attributes->Attributes || + NULL != object_attributes->ObjectName || + NULL != object_attributes->RootDirectory || + NULL != object_attributes->SecurityDescriptor || + NULL != object_attributes->SecurityQualityOfService) { + should_break = true; + } + } + + process_id = static_cast( + reinterpret_cast(client_id->UniqueProcess)); + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + + if (should_break) + break; + + if (!ValidParameter(process, sizeof(HANDLE), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESS_TAG, desired_access, + process_id, &answer); + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + return answer.nt_status; + + __try { + // Write the output parameters. + *process = answer.handle; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + + return answer.nt_status; + } while (false); + + return status; +} + + +NTSTATUS WINAPI TargetNtOpenProcessToken( + NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process, + ACCESS_MASK desired_access, PHANDLE token) { + NTSTATUS status = orig_OpenProcessToken(process, desired_access, token); + if (NT_SUCCESS(status)) + return status; + + do { + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + break; + + if (CURRENT_PROCESS != process) + break; + + if (!ValidParameter(token, sizeof(HANDLE), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKEN_TAG, process, + desired_access, &answer); + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + return answer.nt_status; + + __try { + // Write the output parameters. + *token = answer.handle; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + + return answer.nt_status; + } while (false); + + return status; +} + +NTSTATUS WINAPI TargetNtOpenProcessTokenEx( + NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process, + ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token) { + NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access, + handle_attributes, token); + if (NT_SUCCESS(status)) + return status; + + do { + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + break; + + if (CURRENT_PROCESS != process) + break; + + if (!ValidParameter(token, sizeof(HANDLE), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKENEX_TAG, process, + desired_access, handle_attributes, &answer); + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + return answer.nt_status; + + __try { + // Write the output parameters. + *token = answer.handle; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + + return answer.nt_status; + } while (false); + + return status; +} + +BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW, + LPCWSTR application_name, LPWSTR command_line, + LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, + BOOL inherit_handles, DWORD flags, + LPVOID environment, LPCWSTR current_directory, + LPSTARTUPINFOW startup_info, + LPPROCESS_INFORMATION process_information) { + if (orig_CreateProcessW(application_name, command_line, process_attributes, + thread_attributes, inherit_handles, flags, + environment, current_directory, startup_info, + process_information)) { + return TRUE; + } + DWORD original_error = ::GetLastError(); + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return FALSE; + + do { + if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), + WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + const wchar_t* cur_dir = NULL; + + wchar_t current_directory[MAX_PATH]; + DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); + if (0 != result && result < MAX_PATH) + cur_dir = current_directory; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + + InOutCountedBuffer proc_info(process_information, + sizeof(PROCESS_INFORMATION)); + + ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name, + command_line, cur_dir, proc_info, &answer); + if (SBOX_ALL_OK != code) + break; + + ::SetLastError(answer.win32_result); + if (ERROR_SUCCESS != answer.win32_result) + return FALSE; + + return TRUE; + } while (false); + + ::SetLastError(original_error); + return FALSE; +} + +BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA, + LPCSTR application_name, LPSTR command_line, + LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, + BOOL inherit_handles, DWORD flags, + LPVOID environment, LPCSTR current_directory, + LPSTARTUPINFOA startup_info, + LPPROCESS_INFORMATION process_information) { + if (orig_CreateProcessA(application_name, command_line, process_attributes, + thread_attributes, inherit_handles, flags, + environment, current_directory, startup_info, + process_information)) { + return TRUE; + } + DWORD original_error = ::GetLastError(); + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return FALSE; + + do { + if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), + WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + // Convert the input params to unicode. + UNICODE_STRING *cmd_unicode = NULL; + UNICODE_STRING *app_unicode = NULL; + if (command_line) { + cmd_unicode = AnsiToUnicode(command_line); + if (!cmd_unicode) + break; + } + + if (application_name) { + app_unicode = AnsiToUnicode(application_name); + if (!app_unicode) { + operator delete(cmd_unicode, NT_ALLOC); + break; + } + } + + const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL; + const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL; + const wchar_t* cur_dir = NULL; + + wchar_t current_directory[MAX_PATH]; + DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); + if (0 != result && result < MAX_PATH) + cur_dir = current_directory; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + + InOutCountedBuffer proc_info(process_information, + sizeof(PROCESS_INFORMATION)); + + ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name, + cmd_line, cur_dir, proc_info, &answer); + + operator delete(cmd_unicode, NT_ALLOC); + operator delete(app_unicode, NT_ALLOC); + + if (SBOX_ALL_OK != code) + break; + + ::SetLastError(answer.win32_result); + if (ERROR_SUCCESS != answer.win32_result) + return FALSE; + + return TRUE; + } while (false); + + ::SetLastError(original_error); + return FALSE; +} + +// Creates a thread without registering with CSRSS. This is required if we +// closed the CSRSS ALPC port after lockdown. +HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread, + LPSECURITY_ATTRIBUTES thread_attributes, + SIZE_T stack_size, + LPTHREAD_START_ROUTINE start_address, + PVOID parameter, + DWORD creation_flags, + LPDWORD thread_id) { +// Try the normal CreateThread; switch to RtlCreateUserThread if needed. + static bool use_create_thread = true; + HANDLE thread; + if (use_create_thread) { + thread = orig_CreateThread(thread_attributes, stack_size, start_address, + parameter, creation_flags, thread_id); + if (thread) + return thread; + } + + PSECURITY_DESCRIPTOR sd = + thread_attributes ? thread_attributes->lpSecurityDescriptor : NULL; + CLIENT_ID client_id; + + NTSTATUS result = g_nt.RtlCreateUserThread(NtCurrentProcess, sd, + creation_flags & CREATE_SUSPENDED, + 0, stack_size, 0, start_address, + parameter, &thread, &client_id); + if (!NT_SUCCESS(result)) + return 0; + + // CSRSS is closed if we got here, so use RtlCreateUserThread from here on. + use_create_thread = false; + if (thread_id) + *thread_id = HandleToUlong(client_id.UniqueThread); + return thread; +} + +// Cache the default LCID to avoid pinging CSRSS after lockdown. +// TODO(jschuh): This approach will miss a default locale changes after +// lockdown. In the future we may want to have the broker check instead. +LCID WINAPI TargetGetUserDefaultLCID( + GetUserDefaultLCIDFunction orig_GetUserDefaultLCID) { + static LCID default_lcid = orig_GetUserDefaultLCID(); + return default_lcid; +} + +} // namespace sandbox diff --git a/sandbox/win/src/process_thread_interception.h b/sandbox/win/src/process_thread_interception.h new file mode 100644 index 0000000..37c2c14 --- /dev/null +++ b/sandbox/win/src/process_thread_interception.h @@ -0,0 +1,101 @@ +// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +#ifndef SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__ +#define SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__ + +namespace sandbox { + +extern "C" { + +typedef BOOL (WINAPI *CreateProcessWFunction)( + LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + +typedef BOOL (WINAPI *CreateProcessAFunction)( + LPCSTR lpApplicationName, + LPSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCSTR lpCurrentDirectory, + LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + +typedef HANDLE (WINAPI *CreateThreadFunction)( + LPSECURITY_ATTRIBUTES lpThreadAttributes, + SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, + PVOID lpParameter, + DWORD dwCreationFlags, + LPDWORD lpThreadId); + +typedef LCID (WINAPI *GetUserDefaultLCIDFunction)(); + +// Interception of NtOpenThread on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThread( + NtOpenThreadFunction orig_OpenThread, PHANDLE thread, + ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes, + PCLIENT_ID client_id); + +// Interception of NtOpenProcess on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcess( + NtOpenProcessFunction orig_OpenProcess, PHANDLE process, + ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes, + PCLIENT_ID client_id); + +// Interception of NtOpenProcessToken on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessToken( + NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process, + ACCESS_MASK desired_access, PHANDLE token); + +// Interception of NtOpenProcessTokenEx on the child process. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessTokenEx( + NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process, + ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token); + +// Interception of CreateProcessW and A in kernel32.dll. +SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessW( + CreateProcessWFunction orig_CreateProcessW, LPCWSTR application_name, + LPWSTR command_line, LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, + LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info, + LPPROCESS_INFORMATION process_information); + +SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA( + CreateProcessAFunction orig_CreateProcessA, LPCSTR application_name, + LPSTR command_line, LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags, + LPVOID environment, LPCSTR current_directory, LPSTARTUPINFOA startup_info, + LPPROCESS_INFORMATION process_information); + +// Interception of CreateThread in kernel32.dll. +SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateThread( + CreateThreadFunction orig_CreateThread, + LPSECURITY_ATTRIBUTES thread_attributes, SIZE_T stack_size, + LPTHREAD_START_ROUTINE start_address, PVOID parameter, + DWORD creation_flags, LPDWORD thread_id); + +// Interception of GetUserDefaultLCID in kernel32.dll. +SANDBOX_INTERCEPT LCID WINAPI TargetGetUserDefaultLCID( + GetUserDefaultLCIDFunction orig_GetUserDefaultLCID); + +} // extern "C" + +} // namespace sandbox + +#endif // SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__ diff --git a/sandbox/win/src/process_thread_policy.cc b/sandbox/win/src/process_thread_policy.cc new file mode 100644 index 0000000..ca00916 --- /dev/null +++ b/sandbox/win/src/process_thread_policy.cc @@ -0,0 +1,242 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/process_thread_policy.h" + +#include + +#include "base/memory/scoped_ptr.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/policy_engine_opcodes.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/win_utils.h" + +namespace { + +// These are the only safe rights that can be given to a sandboxed +// process for the process created by the broker. All others are potential +// vectors of privilege elevation. +const DWORD kProcessRights = SYNCHRONIZE | + PROCESS_QUERY_INFORMATION | + PROCESS_QUERY_LIMITED_INFORMATION | + PROCESS_TERMINATE | + PROCESS_SUSPEND_RESUME; + +const DWORD kThreadRights = SYNCHRONIZE | + THREAD_TERMINATE | + THREAD_SUSPEND_RESUME | + THREAD_QUERY_INFORMATION | + THREAD_QUERY_LIMITED_INFORMATION | + THREAD_SET_LIMITED_INFORMATION; + +// Creates a child process and duplicates the handles to 'target_process'. The +// remaining parameters are the same as CreateProcess(). +BOOL CreateProcessExWHelper(HANDLE target_process, BOOL give_full_access, + LPCWSTR lpApplicationName, LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, DWORD dwCreationFlags, + LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) { + if (!::CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, + lpThreadAttributes, bInheritHandles, dwCreationFlags, + lpEnvironment, lpCurrentDirectory, lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + DWORD process_access = kProcessRights; + DWORD thread_access = kThreadRights; + if (give_full_access) { + process_access = PROCESS_ALL_ACCESS; + thread_access = THREAD_ALL_ACCESS; + } + if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hProcess, + target_process, &lpProcessInformation->hProcess, + process_access, FALSE, DUPLICATE_CLOSE_SOURCE)) { + ::CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hThread, + target_process, &lpProcessInformation->hThread, + thread_access, FALSE, DUPLICATE_CLOSE_SOURCE)) { + return FALSE; + } + return TRUE; +} + +} + +namespace sandbox { + +bool ProcessPolicy::GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy) { + scoped_ptr process; + switch (semantics) { + case TargetPolicy::PROCESS_MIN_EXEC: { + process.reset(new PolicyRule(GIVE_READONLY)); + break; + }; + case TargetPolicy::PROCESS_ALL_EXEC: { + process.reset(new PolicyRule(GIVE_ALLACCESS)); + break; + }; + default: { + return false; + }; + } + + if (!process->AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) { + return false; + } + if (!policy->AddRule(IPC_CREATEPROCESSW_TAG, process.get())) { + return false; + } + return true; +} + +NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info, + uint32 desired_access, + uint32 thread_id, + HANDLE* handle) { + *handle = NULL; + + NtOpenThreadFunction NtOpenThread = NULL; + ResolveNTFunctionPtr("NtOpenThread", &NtOpenThread); + + OBJECT_ATTRIBUTES attributes = {0}; + attributes.Length = sizeof(attributes); + CLIENT_ID client_id = {0}; + client_id.UniqueProcess = reinterpret_cast( + static_cast(client_info.process_id)); + client_id.UniqueThread = + reinterpret_cast(static_cast(thread_id)); + + HANDLE local_handle; + NTSTATUS status = NtOpenThread(&local_handle, desired_access, &attributes, + &client_id); + if (NT_SUCCESS(status)) { + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, + client_info.process, handle, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(local_handle); + return STATUS_ACCESS_DENIED; + } + } + + return status; +} + +NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info, + uint32 desired_access, + uint32 process_id, + HANDLE* handle) { + *handle = NULL; + + NtOpenProcessFunction NtOpenProcess = NULL; + ResolveNTFunctionPtr("NtOpenProcess", &NtOpenProcess); + + if (client_info.process_id != process_id) + return STATUS_ACCESS_DENIED; + + OBJECT_ATTRIBUTES attributes = {0}; + attributes.Length = sizeof(attributes); + CLIENT_ID client_id = {0}; + client_id.UniqueProcess = reinterpret_cast( + static_cast(client_info.process_id)); + HANDLE local_handle; + NTSTATUS status = NtOpenProcess(&local_handle, desired_access, &attributes, + &client_id); + if (NT_SUCCESS(status)) { + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, + client_info.process, handle, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(local_handle); + return STATUS_ACCESS_DENIED; + } + } + + return status; +} + +NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info, + HANDLE process, + uint32 desired_access, + HANDLE* handle) { + *handle = NULL; + NtOpenProcessTokenFunction NtOpenProcessToken = NULL; + ResolveNTFunctionPtr("NtOpenProcessToken", &NtOpenProcessToken); + + if (CURRENT_PROCESS != process) + return STATUS_ACCESS_DENIED; + + HANDLE local_handle; + NTSTATUS status = NtOpenProcessToken(client_info.process, desired_access, + &local_handle); + if (NT_SUCCESS(status)) { + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, + client_info.process, handle, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(local_handle); + return STATUS_ACCESS_DENIED; + } + } + return status; +} + +NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info, + HANDLE process, + uint32 desired_access, + uint32 attributes, + HANDLE* handle) { + *handle = NULL; + NtOpenProcessTokenExFunction NtOpenProcessTokenEx = NULL; + ResolveNTFunctionPtr("NtOpenProcessTokenEx", &NtOpenProcessTokenEx); + + if (CURRENT_PROCESS != process) + return STATUS_ACCESS_DENIED; + + HANDLE local_handle; + NTSTATUS status = NtOpenProcessTokenEx(client_info.process, desired_access, + attributes, &local_handle); + if (NT_SUCCESS(status)) { + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, + client_info.process, handle, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(local_handle); + return STATUS_ACCESS_DENIED; + } + } + return status; +} + +DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &app_name, + const std::wstring &command_line, + PROCESS_INFORMATION* process_info) { + // The only action supported is ASK_BROKER which means create the process. + if (GIVE_ALLACCESS != eval_result && GIVE_READONLY != eval_result) { + return ERROR_ACCESS_DENIED; + } + + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + scoped_ptr_malloc cmd_line(_wcsdup(command_line.c_str())); + + BOOL should_give_full_access = (GIVE_ALLACCESS == eval_result); + if (!CreateProcessExWHelper(client_info.process, should_give_full_access, + app_name.c_str(), cmd_line.get(), NULL, NULL, + FALSE, 0, NULL, NULL, &startup_info, + process_info)) { + return ERROR_ACCESS_DENIED; + } + return ERROR_SUCCESS; +} + +} // namespace sandbox diff --git a/sandbox/win/src/process_thread_policy.h b/sandbox/win/src/process_thread_policy.h new file mode 100644 index 0000000..78323cc --- /dev/null +++ b/sandbox/win/src/process_thread_policy.h @@ -0,0 +1,82 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_PROCESS_THREAD_POLICY_H_ +#define SANDBOX_SRC_PROCESS_THREAD_POLICY_H_ + +#include + +#include "sandbox/src/policy_low_level.h" + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sandbox_policy.h" + +namespace sandbox { + +enum EvalResult; + +// This class centralizes most of the knowledge related to process execution. +class ProcessPolicy { + public: + // Creates the required low-level policy rules to evaluate a high-level. + // policy rule for process creation + // 'name' is the executable to be spawn. + // 'semantics' is the desired semantics. + // 'policy' is the policy generator to which the rules are going to be added. + static bool GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy); + + // Opens a thread from the child process and returns the handle. + // client_info contains the information about the child process, + // desired_access is the access requested by the child and thread_id + // is the thread_id to be opened. + // The function returns the return value of NtOpenThread. + static NTSTATUS OpenThreadAction(const ClientInfo& client_info, + uint32 desired_access, + uint32 thread_id, + HANDLE* handle); + + // Opens the process id passed in and returns the duplicated handle to + // the child. We only allow the child processes to open themselves. Any other + // pid open is denied. + static NTSTATUS OpenProcessAction(const ClientInfo& client_info, + uint32 desired_access, + uint32 process_id, + HANDLE* handle); + + // Opens the token associated with the process and returns the duplicated + // handle to the child. We only allow the child processes to open his own + // token (using ::GetCurrentProcess()). + static NTSTATUS OpenProcessTokenAction(const ClientInfo& client_info, + HANDLE process, + uint32 desired_access, + HANDLE* handle); + + // Opens the token associated with the process and returns the duplicated + // handle to the child. We only allow the child processes to open his own + // token (using ::GetCurrentProcess()). + static NTSTATUS OpenProcessTokenExAction(const ClientInfo& client_info, + HANDLE process, + uint32 desired_access, + uint32 attributes, + HANDLE* handle); + + // Processes a 'CreateProcessW()' request from the target. + // 'client_info' : the target process that is making the request. + // 'eval_result' : The desired policy action to accomplish. + // 'app_name' : The full path of the process to be created. + // 'command_line' : The command line passed to the created process. + static DWORD CreateProcessWAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &app_name, + const std::wstring &command_line, + PROCESS_INFORMATION* process_info); +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_PROCESS_THREAD_POLICY_H_ diff --git a/sandbox/win/src/registry_dispatcher.cc b/sandbox/win/src/registry_dispatcher.cc new file mode 100644 index 0000000..f86c76a --- /dev/null +++ b/sandbox/win/src/registry_dispatcher.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/registry_dispatcher.h" + +#include "base/win/scoped_handle.h" +#include "base/win/windows_version.h" +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/policy_broker.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/registry_interception.h" +#include "sandbox/src/registry_policy.h" + +namespace { + +// Builds a path using the root directory and the name. +bool GetCompletePath(HANDLE root, const std::wstring& name, + std::wstring* complete_name) { + if (root) { + if (!sandbox::GetPathFromHandle(root, complete_name)) + return false; + + *complete_name += L"\\"; + *complete_name += name; + } else { + *complete_name = name; + } + + return true; +} + +} + +namespace sandbox { + +RegistryDispatcher::RegistryDispatcher(PolicyBase* policy_base) + : policy_base_(policy_base) { + static const IPCCall create_params = { + {IPC_NTCREATEKEY_TAG, WCHAR_TYPE, ULONG_TYPE, VOIDPTR_TYPE, ULONG_TYPE, + ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast(&RegistryDispatcher::NtCreateKey) + }; + + static const IPCCall open_params = { + {IPC_NTOPENKEY_TAG, WCHAR_TYPE, ULONG_TYPE, VOIDPTR_TYPE, ULONG_TYPE}, + reinterpret_cast(&RegistryDispatcher::NtOpenKey) + }; + + ipc_calls_.push_back(create_params); + ipc_calls_.push_back(open_params); +} + +bool RegistryDispatcher::SetupService(InterceptionManager* manager, + int service) { + if (IPC_NTCREATEKEY_TAG == service) + return INTERCEPT_NT(manager, NtCreateKey, CREATE_KEY_ID, 32); + + if (IPC_NTOPENKEY_TAG == service) { + bool result = INTERCEPT_NT(manager, NtOpenKey, OPEN_KEY_ID, 16); + if (base::win::GetVersion() >= base::win::VERSION_WIN7) + result &= INTERCEPT_NT(manager, NtOpenKeyEx, OPEN_KEY_EX_ID, 20); + return result; + } + + return false; +} + +bool RegistryDispatcher::NtCreateKey( + IPCInfo* ipc, std::wstring* name, DWORD attributes, HANDLE root, + DWORD desired_access, DWORD title_index, DWORD create_options) { + base::win::ScopedHandle root_handle; + std::wstring real_path = *name; + + // If there is a root directory, we need to duplicate the handle to make + // it valid in this process. + if (root) { + if (!::DuplicateHandle(ipc->client_info->process, root, + ::GetCurrentProcess(), &root, 0, FALSE, + DUPLICATE_SAME_ACCESS)) + return false; + + root_handle.Set(root); + } + + if (!GetCompletePath(root, *name, &real_path)) + return false; + + const wchar_t* regname = real_path.c_str(); + CountedParameterSet params; + params[OpenKey::NAME] = ParamPickerMake(regname); + params[OpenKey::ACCESS] = ParamPickerMake(desired_access); + + EvalResult result = policy_base_->EvalPolicy(IPC_NTCREATEKEY_TAG, + params.GetBase()); + + HANDLE handle; + NTSTATUS nt_status; + ULONG disposition = 0; + if (!RegistryPolicy::CreateKeyAction(result, *ipc->client_info, *name, + attributes, root, desired_access, + title_index, create_options, &handle, + &nt_status, &disposition)) { + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + // Return operation status on the IPC. + ipc->return_info.extended[0].unsigned_int = disposition; + ipc->return_info.nt_status = nt_status; + ipc->return_info.handle = handle; + return true; +} + +bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc, std::wstring* name, + DWORD attributes, HANDLE root, + DWORD desired_access) { + base::win::ScopedHandle root_handle; + std::wstring real_path = *name; + + // If there is a root directory, we need to duplicate the handle to make + // it valid in this process. + if (root) { + if (!::DuplicateHandle(ipc->client_info->process, root, + ::GetCurrentProcess(), &root, 0, FALSE, + DUPLICATE_SAME_ACCESS)) + return false; + root_handle.Set(root); + } + + if (!GetCompletePath(root, *name, &real_path)) + return false; + + const wchar_t* regname = real_path.c_str(); + CountedParameterSet params; + params[OpenKey::NAME] = ParamPickerMake(regname); + params[OpenKey::ACCESS] = ParamPickerMake(desired_access); + + EvalResult result = policy_base_->EvalPolicy(IPC_NTOPENKEY_TAG, + params.GetBase()); + HANDLE handle; + NTSTATUS nt_status; + if (!RegistryPolicy::OpenKeyAction(result, *ipc->client_info, *name, + attributes, root, desired_access, &handle, + &nt_status)) { + ipc->return_info.nt_status = STATUS_ACCESS_DENIED; + return true; + } + + // Return operation status on the IPC. + ipc->return_info.nt_status = nt_status; + ipc->return_info.handle = handle; + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/registry_dispatcher.h b/sandbox/win/src/registry_dispatcher.h new file mode 100644 index 0000000..6d1390d --- /dev/null +++ b/sandbox/win/src/registry_dispatcher.h @@ -0,0 +1,39 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_REGISTRY_DISPATCHER_H_ +#define SANDBOX_SRC_REGISTRY_DISPATCHER_H_ + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sandbox_policy_base.h" + +namespace sandbox { + +// This class handles registry-related IPC calls. +class RegistryDispatcher : public Dispatcher { + public: + explicit RegistryDispatcher(PolicyBase* policy_base); + ~RegistryDispatcher() {} + + // Dispatcher interface. + virtual bool SetupService(InterceptionManager* manager, int service); + + private: + // Processes IPC requests coming from calls to NtCreateKey in the target. + bool NtCreateKey(IPCInfo* ipc, std::wstring* name, DWORD attributes, + HANDLE root, DWORD desired_access, + DWORD title_index, DWORD create_options); + + // Processes IPC requests coming from calls to NtOpenKey in the target. + bool NtOpenKey(IPCInfo* ipc, std::wstring* name, DWORD attributes, + HANDLE root, DWORD desired_access); + + PolicyBase* policy_base_; + DISALLOW_COPY_AND_ASSIGN(RegistryDispatcher); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_REGISTRY_DISPATCHER_H_ diff --git a/sandbox/win/src/registry_interception.cc b/sandbox/win/src/registry_interception.cc new file mode 100644 index 0000000..3cabf47 --- /dev/null +++ b/sandbox/win/src/registry_interception.cc @@ -0,0 +1,176 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/registry_interception.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +NTSTATUS WINAPI TargetNtCreateKey(NtCreateKeyFunction orig_CreateKey, + PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + ULONG title_index, PUNICODE_STRING class_name, + ULONG create_options, PULONG disposition) { + // Check if the process can create it first. + NTSTATUS status = orig_CreateKey(key, desired_access, object_attributes, + title_index, class_name, create_options, + disposition); + if (NT_SUCCESS(status)) + return status; + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return status; + + do { + if (!ValidParameter(key, sizeof(HANDLE), WRITE)) + break; + + if (disposition && !ValidParameter(disposition, sizeof(ULONG), WRITE)) + break; + + // At this point we don't support class_name. + if (class_name && class_name->Buffer && class_name->Length) + break; + + // We don't support creating link keys, volatile keys and backup/restore. + if (create_options) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + wchar_t* name; + uint32 attributes = 0; + HANDLE root_directory = 0; + NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, + &root_directory); + if (!NT_SUCCESS(ret) || NULL == name) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + + ResultCode code = CrossCall(ipc, IPC_NTCREATEKEY_TAG, name, attributes, + root_directory, desired_access, title_index, + create_options, &answer); + + operator delete(name, NT_ALLOC); + + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + // TODO(nsylvain): We should return answer.nt_status here instead + // of status. We can do this only after we checked the policy. + // otherwise we will returns ACCESS_DENIED for all paths + // that are not specified by a policy, even though your token allows + // access to that path, and the original call had a more meaningful + // error. Bug 4369 + break; + + __try { + *key = answer.handle; + + if (disposition) + *disposition = answer.extended[0].unsigned_int; + + status = answer.nt_status; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + } while (false); + + return status; +} + +NTSTATUS WINAPI CommonNtOpenKey(NTSTATUS status, PHANDLE key, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes) { + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return status; + + do { + if (!ValidParameter(key, sizeof(HANDLE), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + wchar_t* name; + uint32 attributes; + HANDLE root_directory; + NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, + &root_directory); + if (!NT_SUCCESS(ret) || NULL == name) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_NTOPENKEY_TAG, name, attributes, + root_directory, desired_access, &answer); + + operator delete(name, NT_ALLOC); + + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + // TODO(nsylvain): We should return answer.nt_status here instead + // of status. We can do this only after we checked the policy. + // otherwise we will returns ACCESS_DENIED for all paths + // that are not specified by a policy, even though your token allows + // access to that path, and the original call had a more meaningful + // error. Bug 4369 + break; + + __try { + *key = answer.handle; + status = answer.nt_status; + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } + } while (false); + + return status; +} + +NTSTATUS WINAPI TargetNtOpenKey(NtOpenKeyFunction orig_OpenKey, PHANDLE key, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes) { + // Check if the process can open it first. + NTSTATUS status = orig_OpenKey(key, desired_access, object_attributes); + if (NT_SUCCESS(status)) + return status; + + return CommonNtOpenKey(status, key, desired_access, object_attributes); +} + +NTSTATUS WINAPI TargetNtOpenKeyEx(NtOpenKeyExFunction orig_OpenKeyEx, + PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + ULONG open_options) { + // Check if the process can open it first. + NTSTATUS status = orig_OpenKeyEx(key, desired_access, object_attributes, + open_options); + + // We do not support open_options at this time. The 2 current known values + // are REG_OPTION_CREATE_LINK, to open a symbolic link, and + // REG_OPTION_BACKUP_RESTORE to open the key with special privileges. + if (NT_SUCCESS(status) || open_options != 0) + return status; + + return CommonNtOpenKey(status, key, desired_access, object_attributes); +} + +} // namespace sandbox diff --git a/sandbox/win/src/registry_interception.h b/sandbox/win/src/registry_interception.h new file mode 100644 index 0000000..7036c49 --- /dev/null +++ b/sandbox/win/src/registry_interception.h @@ -0,0 +1,38 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +#ifndef SANDBOX_SRC_REGISTRY_INTERCEPTION_H__ +#define SANDBOX_SRC_REGISTRY_INTERCEPTION_H__ + +namespace sandbox { + +extern "C" { + +// Interception of NtCreateKey on the child process. +// It should never be called directly +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey( + NtCreateKeyFunction orig_CreateKey, PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, ULONG title_index, + PUNICODE_STRING class_name, ULONG create_options, PULONG disposition); + +// Interception of NtOpenKey on the child process. +// It should never be called directly +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey( + NtOpenKeyFunction orig_OpenKey, PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes); + +// Interception of NtOpenKeyEx on the child process. +// It should never be called directly +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx( + NtOpenKeyExFunction orig_OpenKeyEx, PHANDLE key, ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, ULONG open_options); + +} // extern "C" + +} // namespace sandbox + +#endif // SANDBOX_SRC_REGISTRY_INTERCEPTION_H__ diff --git a/sandbox/win/src/registry_policy.cc b/sandbox/win/src/registry_policy.cc new file mode 100644 index 0000000..497636f --- /dev/null +++ b/sandbox/win/src/registry_policy.cc @@ -0,0 +1,227 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "sandbox/src/registry_policy.h" + +#include "base/logging.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_engine_opcodes.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox_utils.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/win_utils.h" + +namespace { + +static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | + KEY_NOTIFY | KEY_READ | GENERIC_READ | + GENERIC_EXECUTE | READ_CONTROL; + +// Opens the key referenced by |obj_attributes| with |access| and +// checks what permission was given. Remove the WRITE flags and update +// |access| with the new value. +NTSTATUS TranslateMaximumAllowed(OBJECT_ATTRIBUTES* obj_attributes, + DWORD* access) { + NtOpenKeyFunction NtOpenKey = NULL; + ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey); + + NtCloseFunction NtClose = NULL; + ResolveNTFunctionPtr("NtClose", &NtClose); + + NtQueryObjectFunction NtQueryObject = NULL; + ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject); + + // Open the key. + HANDLE handle; + NTSTATUS status = NtOpenKey(&handle, *access, obj_attributes); + if (!NT_SUCCESS(status)) + return status; + + OBJECT_BASIC_INFORMATION info = {0}; + status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info), + NULL); + NtClose(handle); + if (!NT_SUCCESS(status)) + return status; + + *access = info.GrantedAccess & kAllowedRegFlags; + return STATUS_SUCCESS; +} + +NTSTATUS NtCreateKeyInTarget(HANDLE* target_key_handle, + ACCESS_MASK desired_access, + OBJECT_ATTRIBUTES* obj_attributes, + ULONG title_index, + UNICODE_STRING* class_name, + ULONG create_options, + ULONG* disposition, + HANDLE target_process) { + NtCreateKeyFunction NtCreateKey = NULL; + ResolveNTFunctionPtr("NtCreateKey", &NtCreateKey); + + if (MAXIMUM_ALLOWED & desired_access) { + NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access); + if (!NT_SUCCESS(status)) + return STATUS_ACCESS_DENIED; + } + + HANDLE local_handle = INVALID_HANDLE_VALUE; + NTSTATUS status = NtCreateKey(&local_handle, desired_access, obj_attributes, + title_index, class_name, create_options, + disposition); + if (!NT_SUCCESS(status)) + return status; + + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, + target_process, target_key_handle, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(local_handle); + return STATUS_ACCESS_DENIED; + } + return STATUS_SUCCESS; +} + +NTSTATUS NtOpenKeyInTarget(HANDLE* target_key_handle, + ACCESS_MASK desired_access, + OBJECT_ATTRIBUTES* obj_attributes, + HANDLE target_process) { + NtOpenKeyFunction NtOpenKey = NULL; + ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey); + + if (MAXIMUM_ALLOWED & desired_access) { + NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access); + if (!NT_SUCCESS(status)) + return STATUS_ACCESS_DENIED; + } + + HANDLE local_handle = INVALID_HANDLE_VALUE; + NTSTATUS status = NtOpenKey(&local_handle, desired_access, obj_attributes); + + if (!NT_SUCCESS(status)) + return status; + + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, + target_process, target_key_handle, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(local_handle); + return STATUS_ACCESS_DENIED; + } + return STATUS_SUCCESS; +} + +} + +namespace sandbox { + +bool RegistryPolicy::GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy) { + std::wstring resovled_name(name); + if (resovled_name.empty()) { + return false; + } + + if (!ResolveRegistryName(resovled_name, &resovled_name)) + return false; + + name = resovled_name.c_str(); + + EvalResult result = ASK_BROKER; + + PolicyRule open(result); + PolicyRule create(result); + + switch (semantics) { + case TargetPolicy::REG_ALLOW_READONLY: { + // We consider all flags that are not known to be readonly as potentially + // used for write. Here we also support MAXIMUM_ALLOWED, but we are going + // to expand it to read-only before the call. + DWORD restricted_flags = ~(kAllowedRegFlags | MAXIMUM_ALLOWED); + open.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); + create.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); + break; + } + case TargetPolicy::REG_ALLOW_ANY: { + break; + } + default: { + NOTREACHED(); + return false; + } + } + + if (!create.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) || + !policy->AddRule(IPC_NTCREATEKEY_TAG, &create)) { + return false; + } + + if (!open.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) || + !policy->AddRule(IPC_NTOPENKEY_TAG, &open)) { + return false; + } + + return true; +} + +bool RegistryPolicy::CreateKeyAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &key, + uint32 attributes, + HANDLE root_directory, + uint32 desired_access, + uint32 title_index, + uint32 create_options, + HANDLE* handle, + NTSTATUS* nt_status, + ULONG* disposition) { + // The only action supported is ASK_BROKER which means create the requested + // file as specified. + if (ASK_BROKER != eval_result) { + *nt_status = STATUS_ACCESS_DENIED; + return false; + } + + // We don't support creating link keys, volatile keys or backup/restore. + if (create_options) { + *nt_status = STATUS_ACCESS_DENIED; + return false; + } + + UNICODE_STRING uni_name = {0}; + OBJECT_ATTRIBUTES obj_attributes = {0}; + InitObjectAttribs(key, attributes, root_directory, &obj_attributes, + &uni_name); + *nt_status = NtCreateKeyInTarget(handle, desired_access, &obj_attributes, + title_index, NULL, create_options, + disposition, client_info.process); + return true; +} + +bool RegistryPolicy::OpenKeyAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &key, + uint32 attributes, + HANDLE root_directory, + uint32 desired_access, + HANDLE* handle, + NTSTATUS* nt_status) { + // The only action supported is ASK_BROKER which means open the requested + // file as specified. + if (ASK_BROKER != eval_result) { + *nt_status = STATUS_ACCESS_DENIED; + return true; + } + + UNICODE_STRING uni_name = {0}; + OBJECT_ATTRIBUTES obj_attributes = {0}; + InitObjectAttribs(key, attributes, root_directory, &obj_attributes, + &uni_name); + *nt_status = NtOpenKeyInTarget(handle, desired_access, &obj_attributes, + client_info.process); + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/registry_policy.h b/sandbox/win/src/registry_policy.h new file mode 100644 index 0000000..da41d1c --- /dev/null +++ b/sandbox/win/src/registry_policy.h @@ -0,0 +1,57 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_REGISTRY_POLICY_H__ +#define SANDBOX_SRC_REGISTRY_POLICY_H__ + +#include + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/policy_low_level.h" +#include "sandbox/src/sandbox_policy.h" + +namespace sandbox { + +enum EvalResult; + +// This class centralizes most of the knowledge related to registry policy +class RegistryPolicy { + public: + // Creates the required low-level policy rules to evaluate a high-level + // policy rule for registry IO, in particular open or create actions. + static bool GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy); + + // Performs the desired policy action on a create request with an + // API that is compatible with the IPC-received parameters. + static bool CreateKeyAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &key, + uint32 attributes, + HANDLE root_directory, + uint32 desired_access, + uint32 title_index, + uint32 create_options, + HANDLE* handle, + NTSTATUS* nt_status, + ULONG* disposition); + + // Performs the desired policy action on an open request with an + // API that is compatible with the IPC-received parameters. + static bool OpenKeyAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &key, + uint32 attributes, + HANDLE root_directory, + uint32 desired_access, + HANDLE* handle, + NTSTATUS* nt_status); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_REGISTRY_POLICY_H__ diff --git a/sandbox/win/src/registry_policy_test.cc b/sandbox/win/src/registry_policy_test.cc new file mode 100644 index 0000000..cdc1577 --- /dev/null +++ b/sandbox/win/src/registry_policy_test.cc @@ -0,0 +1,289 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/src/registry_policy.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/win_utils.h" +#include "sandbox/tests/common/controller.h" + +namespace { + +static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | + KEY_NOTIFY | KEY_READ | GENERIC_READ | + GENERIC_EXECUTE | READ_CONTROL; + +#define BINDNTDLL(name) \ + name ## Function name = reinterpret_cast( \ + ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) + +bool IsKeyOpenForRead(HKEY handle) { + BINDNTDLL(NtQueryObject); + + OBJECT_BASIC_INFORMATION info = {0}; + NTSTATUS status = NtQueryObject(handle, ObjectBasicInformation, &info, + sizeof(info), NULL); + + if (!NT_SUCCESS(status)) + return false; + + if ((info.GrantedAccess & (~kAllowedRegFlags)) != 0) + return false; + return true; +} + +} + +namespace sandbox { + +SBOX_TESTS_COMMAND int Reg_OpenKey(int argc, wchar_t **argv) { + if (argc != 4) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + REGSAM desired_access = 0; + ULONG options = 0; + if (wcscmp(argv[1], L"read") == 0) { + desired_access = KEY_READ; + } else if (wcscmp(argv[1], L"write") == 0) { + desired_access = KEY_ALL_ACCESS; + } else if (wcscmp(argv[1], L"link") == 0) { + options = REG_OPTION_CREATE_LINK; + desired_access = KEY_ALL_ACCESS; + } else { + desired_access = MAXIMUM_ALLOWED; + } + + HKEY root = GetReservedKeyFromName(argv[2]); + HKEY key; + LRESULT result = 0; + + if (wcscmp(argv[0], L"create") == 0) + result = ::RegCreateKeyEx(root, argv[3], 0, NULL, options, desired_access, + NULL, &key, NULL); + else + result = ::RegOpenKeyEx(root, argv[3], 0, desired_access, &key); + + if (ERROR_SUCCESS == result) { + if (MAXIMUM_ALLOWED == desired_access) { + if (!IsKeyOpenForRead(key)) { + ::RegCloseKey(key); + return SBOX_TEST_FAILED; + } + } + ::RegCloseKey(key); + return SBOX_TEST_SUCCEEDED; + } else if (ERROR_ACCESS_DENIED == result) { + return SBOX_TEST_DENIED; + } + + return SBOX_TEST_FAILED; +} + +TEST(RegistryPolicyTest, TestKeyAnyAccess) { + TestRunner runner; + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_LOCAL_MACHINE")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_ANY, + L"HKEY_LOCAL_MACHINE\\Software\\Microsoft")); + + // Tests read access on key allowed for read-write. + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( + L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( + L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft")); + + if (::IsUserAnAdmin()) { + // Tests write access on key allowed for read-write. + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( + L"Reg_OpenKey create write HKEY_LOCAL_MACHINE software\\microsoft")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( + L"Reg_OpenKey open write HKEY_LOCAL_MACHINE software\\microsoft")); + } + + // Tests subdirectory access on keys where we don't have subdirectory acess. + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create read " + L"HKEY_LOCAL_MACHINE software\\microsoft\\Windows")); + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey open read " + L"HKEY_LOCAL_MACHINE software\\microsoft\\windows")); + + // Tests to see if we can create keys where we dont have subdirectory access. + // This is denied. + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create write " + L"HKEY_LOCAL_MACHINE software\\Microsoft\\google_unit_tests")); + + RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Microsoft\\google_unit_tests"); + + // Tests if we need to handle differently the "\\" at the end. + // This is denied. We need to add both rules. + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( + L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft\\")); + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( + L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft\\")); +} + +TEST(RegistryPolicyTest, TestKeyNoAccess) { + TestRunner runner; + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_LOCAL_MACHINE")); + + // Tests read access where we don't have access at all. + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( + L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software")); + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( + L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software")); +} + +TEST(RegistryPolicyTest, TestKeyReadOnlyAccess) { + TestRunner runner; + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_LOCAL_MACHINE")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_LOCAL_MACHINE\\Software\\Policies")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*")); + + // Tests subdirectory acess on keys where we have subdirectory acess. + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create read " + L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey open read " + L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft")); + + // Tests to see if we can create keys where we have subdirectory access. + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create write " + L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests")); + + RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests"); +} + +TEST(RegistryPolicyTest, TestKeyAllAccessSubDir) { + TestRunner runner; + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_LOCAL_MACHINE")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_ANY, + L"HKEY_LOCAL_MACHINE\\Software\\Policies")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_ANY, + L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*")); + + if (::IsUserAnAdmin()) { + // Tests to see if we can create keys where we have subdirectory access. + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create write " + L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests")); + + RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests"); + } +} + +TEST(RegistryPolicyTest, TestKeyCreateLink) { + TestRunner runner; + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_LOCAL_MACHINE")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_ANY, + L"HKEY_LOCAL_MACHINE\\Software\\Policies")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_ANY, + L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*")); + + // Tests to see if we can create a registry link key. + // NOTE: In theory here we should make sure to check for SBOX_TEST_DENIED + // instead of !SBOX_TEST_SUCCEEDED, but unfortunately the result is not + // access denied. Internally RegCreateKeyEx (At least on Vista 64) tries to + // create the link, and we return successfully access denied, then, it + // decides to try to break the path in multiple chunks, and create the links + // one by one. In this scenario, it tries to create "HKLM\Software" as a + // link key, which obviously fail with STATUS_OBJECT_NAME_COLLISION, and + // this is what is returned to the user. + EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create link " + L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests")); + + // In case our code fails, and the call works, we need to delete the new + // link. There is no api for this, so we need to use the NT call. + HKEY key = NULL; + LRESULT result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, + L"software\\Policies\\google_unit_tests", + REG_OPTION_OPEN_LINK, MAXIMUM_ALLOWED, + &key); + + if (!result) { + HMODULE ntdll = GetModuleHandle(L"ntdll.dll"); + NtDeleteKeyFunction NtDeleteKey = + reinterpret_cast(GetProcAddress(ntdll, + "NtDeleteKey")); + NtDeleteKey(key); + } +} + +TEST(RegistryPolicyTest, TestKeyReadOnlyHKCU) { + TestRunner runner; + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_CURRENT_USER")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_CURRENT_USER\\Software")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_USERS\\.default")); + + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_READONLY, + L"HKEY_USERS\\.default\\software")); + + // Tests read access where we only have read-only access. + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( + L"Reg_OpenKey create read HKEY_CURRENT_USER software")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( + L"Reg_OpenKey open read HKEY_CURRENT_USER software")); + + // Tests write access where we only have read-only acess. + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( + L"Reg_OpenKey create write HKEY_CURRENT_USER software")); + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest( + L"Reg_OpenKey open write HKEY_CURRENT_USER software")); + + // Tests maximum allowed access where we only have read-only access. + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( + L"Reg_OpenKey create maximum_allowed HKEY_CURRENT_USER software")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest( + L"Reg_OpenKey open maximum_allowed HKEY_CURRENT_USER software")); +} + +} // namespace sandbox diff --git a/sandbox/win/src/resolver.cc b/sandbox/win/src/resolver.cc new file mode 100644 index 0000000..295bfb2 --- /dev/null +++ b/sandbox/win/src/resolver.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/resolver.h" + +#include "base/win/pe_image.h" +#include "sandbox/src/sandbox_nt_util.h" + +namespace sandbox { + +NTSTATUS ResolverThunk::Init(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes) { + if (NULL == thunk_storage || 0 == storage_bytes || + NULL == target_module || NULL == target_name) + return STATUS_INVALID_PARAMETER; + + if (storage_bytes < GetThunkSize()) + return STATUS_BUFFER_TOO_SMALL; + + NTSTATUS ret = STATUS_SUCCESS; + if (NULL == interceptor_entry_point) { + ret = ResolveInterceptor(interceptor_module, interceptor_name, + &interceptor_entry_point); + if (!NT_SUCCESS(ret)) + return ret; + } + + ret = ResolveTarget(target_module, target_name, &target_); + if (!NT_SUCCESS(ret)) + return ret; + + interceptor_ = interceptor_entry_point; + + return ret; +} + +NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module, + const char* interceptor_name, + const void** address) { + DCHECK_NT(address); + if (!interceptor_module) + return STATUS_INVALID_PARAMETER; + + base::win::PEImage pe(interceptor_module); + if (!pe.VerifyMagic()) + return STATUS_INVALID_IMAGE_FORMAT; + + *address = pe.GetProcAddress(interceptor_name); + + if (!(*address)) + return STATUS_PROCEDURE_NOT_FOUND; + + return STATUS_SUCCESS; +} + +} // namespace sandbox diff --git a/sandbox/win/src/resolver.h b/sandbox/win/src/resolver.h new file mode 100644 index 0000000..7ec0bf5 --- /dev/null +++ b/sandbox/win/src/resolver.h @@ -0,0 +1,105 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines ResolverThunk, the interface for classes that perform interceptions. +// For more details see +// http://dev.chromium.org/developers/design-documents/sandbox . + +#include "base/basictypes.h" +#include "sandbox/src/nt_internals.h" + +#ifndef SANDBOX_SRC_RESOLVER_H__ +#define SANDBOX_SRC_RESOLVER_H__ + +namespace sandbox { + +// A resolver is the object in charge of performing the actual interception of +// a function. There should be a concrete implementation of a resolver roughly +// per type of interception. +class ResolverThunk { + public: + ResolverThunk() {} + virtual ~ResolverThunk() {} + + // Performs the actual interception of a function. + // target_name is an exported function from the module loaded at + // target_module, and must be replaced by interceptor_name, exported from + // interceptor_module. interceptor_entry_point can be provided instead of + // interceptor_name / interceptor_module. + // thunk_storage must point to a buffer on the child's address space, to hold + // the patch thunk, and related data. If provided, storage_used will receive + // the number of bytes used from thunk_storage. + // + // Example: (without error checking) + // + // size_t size = resolver.GetThunkSize(); + // char* buffer = ::VirtualAllocEx(child_process, NULL, size, + // MEM_COMMIT, PAGE_READWRITE); + // resolver.Setup(ntdll_module, NULL, L"NtCreateFile", NULL, + // &MyReplacementFunction, buffer, size, NULL); + // + // In general, the idea is to allocate a single big buffer for all + // interceptions on the same dll, and call Setup n times. + // WARNING: This means that any data member that is specific to a single + // interception must be reset within this method. + virtual NTSTATUS Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used) = 0; + + // Gets the address of function_name inside module (main exe). + virtual NTSTATUS ResolveInterceptor(const void* module, + const char* function_name, + const void** address); + + // Gets the address of an exported function_name inside module. + virtual NTSTATUS ResolveTarget(const void* module, + const char* function_name, + void** address); + + // Gets the required buffer size for this type of thunk. + virtual size_t GetThunkSize() const = 0; + + protected: + // Performs basic initialization on behalf of a concrete instance of a + // resolver. That is, parameter validation and resolution of the target + // and the interceptor into the member variables. + // + // target_name is an exported function from the module loaded at + // target_module, and must be replaced by interceptor_name, exported from + // interceptor_module. interceptor_entry_point can be provided instead of + // interceptor_name / interceptor_module. + // thunk_storage must point to a buffer on the child's address space, to hold + // the patch thunk, and related data. + virtual NTSTATUS Init(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes); + + // Gets the required buffer size for the internal part of the thunk. + size_t GetInternalThunkSize() const; + + // Initializes the internal part of the thunk. + // interceptor is the function to be called instead of original_function. + bool SetInternalThunk(void* storage, size_t storage_bytes, + const void* original_function, const void* interceptor); + + // Holds the resolved interception target. + void* target_; + // Holds the resolved interception interceptor. + const void* interceptor_; + + DISALLOW_COPY_AND_ASSIGN(ResolverThunk); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_RESOLVER_H__ diff --git a/sandbox/win/src/resolver_32.cc b/sandbox/win/src/resolver_32.cc new file mode 100644 index 0000000..ae5c168 --- /dev/null +++ b/sandbox/win/src/resolver_32.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/resolver.h" + +#include "sandbox/src/sandbox_nt_util.h" + +namespace { + +#pragma pack(push, 1) +struct InternalThunk { + // This struct contains roughly the following code: + // sub esp, 8 // Create working space + // push edx // Save register + // mov edx, [esp + 0xc] // Get return adddress + // mov [esp + 8], edx // Store return address + // mov dword ptr [esp + 0xc], 0x7c401200 // Store extra argument + // mov dword ptr [esp + 4], 0x40010203 // Store address to jump to + // pop edx // Restore register + // ret // Jump to interceptor + // + // This code only modifies esp and eip so it must work with to normal calling + // convention. It is assembled as: + // + // 00 83ec08 sub esp,8 + // 03 52 push edx + // 04 8b54240c mov edx,dword ptr [esp + 0Ch] + // 08 89542408 mov dword ptr [esp + 8], edx + // 0c c744240c0012407c mov dword ptr [esp + 0Ch], 7C401200h + // 14 c744240403020140 mov dword ptr [esp + 4], 40010203h + // 1c 5a pop edx + // 1d c3 ret + InternalThunk() { + opcodes_1 = 0x5208ec83; + opcodes_2 = 0x0c24548b; + opcodes_3 = 0x08245489; + opcodes_4 = 0x0c2444c7; + opcodes_5 = 0x042444c7; + opcodes_6 = 0xc35a; + extra_argument = 0; + interceptor_function = 0; + }; + ULONG opcodes_1; // = 0x5208ec83 + ULONG opcodes_2; // = 0x0c24548b + ULONG opcodes_3; // = 0x08245489 + ULONG opcodes_4; // = 0x0c2444c7 + ULONG extra_argument; + ULONG opcodes_5; // = 0x042444c7 + ULONG interceptor_function; + USHORT opcodes_6; // = 0xc35a +}; +#pragma pack(pop) + +}; // namespace + +namespace sandbox { + +bool ResolverThunk::SetInternalThunk(void* storage, size_t storage_bytes, + const void* original_function, + const void* interceptor) { + if (storage_bytes < sizeof(InternalThunk)) + return false; + + InternalThunk* thunk = new(storage, NT_PLACE) InternalThunk; + +#pragma warning(push) +#pragma warning(disable: 4311) + // These casts generate warnings because they are 32 bit specific. + thunk->interceptor_function = reinterpret_cast(interceptor); + thunk->extra_argument = reinterpret_cast(original_function); +#pragma warning(pop) + + return true; +} + +size_t ResolverThunk::GetInternalThunkSize() const { + return sizeof(InternalThunk); +} + +NTSTATUS ResolverThunk::ResolveTarget(const void* module, + const char* function_name, + void** address) { + const void** casted = const_cast(address); + return ResolverThunk::ResolveInterceptor(module, function_name, casted); +} + +} // namespace sandbox diff --git a/sandbox/win/src/resolver_64.cc b/sandbox/win/src/resolver_64.cc new file mode 100644 index 0000000..96d039b --- /dev/null +++ b/sandbox/win/src/resolver_64.cc @@ -0,0 +1,69 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/resolver.h" + +#include "sandbox/src/sandbox_nt_util.h" + +namespace { + +const BYTE kPushRax = 0x50; +const USHORT kMovRax = 0xB848; +const ULONG kMovRspRax = 0x24048948; +const BYTE kRetNp = 0xC3; + +#pragma pack(push, 1) +struct InternalThunk { + // This struct contains roughly the following code: + // 00 50 push rax + // 01 48b8f0debc9a78563412 mov rax,123456789ABCDEF0h + // 0b 48890424 mov qword ptr [rsp],rax + // 0f c3 ret + // + // The code modifies rax, but that should not be an issue for the common + // calling conventions. + + InternalThunk() { + push_rax = kPushRax; + mov_rax = kMovRax; + interceptor_function = 0; + mov_rsp_rax = kMovRspRax; + ret = kRetNp; + }; + BYTE push_rax; // = 50 + USHORT mov_rax; // = 48 B8 + ULONG_PTR interceptor_function; + ULONG mov_rsp_rax; // = 48 89 04 24 + BYTE ret; // = C3 +}; +#pragma pack(pop) + +} // namespace. + +namespace sandbox { + +size_t ResolverThunk::GetInternalThunkSize() const { + return sizeof(InternalThunk); +} + +bool ResolverThunk::SetInternalThunk(void* storage, size_t storage_bytes, + const void* original_function, + const void* interceptor) { + if (storage_bytes < sizeof(InternalThunk)) + return false; + + InternalThunk* thunk = new(storage, NT_PLACE) InternalThunk; + thunk->interceptor_function = reinterpret_cast(interceptor); + + return true; +} + +NTSTATUS ResolverThunk::ResolveTarget(const void* module, + const char* function_name, + void** address) { + // We don't support sidestep & co. + return STATUS_NOT_IMPLEMENTED; +} + +} // namespace sandbox diff --git a/sandbox/win/src/restricted_token.cc b/sandbox/win/src/restricted_token.cc new file mode 100644 index 0000000..bac8816 --- /dev/null +++ b/sandbox/win/src/restricted_token.cc @@ -0,0 +1,466 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/restricted_token.h" + +#include + +#include "base/logging.h" +#include "sandbox/src/acl.h" +#include "sandbox/src/win_utils.h" + + +namespace sandbox { + +unsigned RestrictedToken::Init(const HANDLE effective_token) { + DCHECK(!init_); + if (init_) + return ERROR_ALREADY_INITIALIZED; + + if (effective_token) { + // We duplicate the handle to be able to use it even if the original handle + // is closed. + HANDLE effective_token_dup; + if (::DuplicateHandle(::GetCurrentProcess(), + effective_token, + ::GetCurrentProcess(), + &effective_token_dup, + DUPLICATE_SAME_ACCESS, + FALSE, + 0)) { // no special options + effective_token_ = effective_token_dup; + } else { + return ::GetLastError(); + } + } else { + if (!::OpenProcessToken(::GetCurrentProcess(), + TOKEN_ALL_ACCESS, + &effective_token_)) + return ::GetLastError(); + } + + init_ = true; + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::GetRestrictedTokenHandle(HANDLE *token_handle) const { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + size_t deny_size = sids_for_deny_only_.size(); + size_t restrict_size = sids_to_restrict_.size(); + size_t privileges_size = privileges_to_disable_.size(); + + SID_AND_ATTRIBUTES *deny_only_array = NULL; + if (deny_size) { + deny_only_array = new SID_AND_ATTRIBUTES[deny_size]; + + for (unsigned int i = 0; i < sids_for_deny_only_.size() ; ++i) { + deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY; + deny_only_array[i].Sid = + const_cast(sids_for_deny_only_[i].GetPSID()); + } + } + + SID_AND_ATTRIBUTES *sids_to_restrict_array = NULL; + if (restrict_size) { + sids_to_restrict_array = new SID_AND_ATTRIBUTES[restrict_size]; + + for (unsigned int i = 0; i < restrict_size; ++i) { + sids_to_restrict_array[i].Attributes = 0; + sids_to_restrict_array[i].Sid = + const_cast(sids_to_restrict_[i].GetPSID()); + } + } + + LUID_AND_ATTRIBUTES *privileges_to_disable_array = NULL; + if (privileges_size) { + privileges_to_disable_array = new LUID_AND_ATTRIBUTES[privileges_size]; + + for (unsigned int i = 0; i < privileges_size; ++i) { + privileges_to_disable_array[i].Attributes = 0; + privileges_to_disable_array[i].Luid = privileges_to_disable_[i]; + } + } + + BOOL result = TRUE; + HANDLE new_token = NULL; + // The SANDBOX_INERT flag did nothing in XP and it was just a way to tell + // if a token has ben restricted given the limiations of IsTokenRestricted() + // but it appears that in Windows 7 it hints the AppLocker subsystem to + // leave us alone. + if (deny_size || restrict_size || privileges_size) { + result = ::CreateRestrictedToken(effective_token_, + SANDBOX_INERT, + static_cast(deny_size), + deny_only_array, + static_cast(privileges_size), + privileges_to_disable_array, + static_cast(restrict_size), + sids_to_restrict_array, + &new_token); + } else { + // Duplicate the token even if it's not modified at this point + // because any subsequent changes to this token would also affect the + // current process. + result = ::DuplicateTokenEx(effective_token_, TOKEN_ALL_ACCESS, NULL, + SecurityIdentification, TokenPrimary, + &new_token); + } + + if (deny_only_array) + delete[] deny_only_array; + + if (sids_to_restrict_array) + delete[] sids_to_restrict_array; + + if (privileges_to_disable_array) + delete[] privileges_to_disable_array; + + if (!result) + return ::GetLastError(); + + // Modify the default dacl on the token to contain Restricted and the user. + if (!AddSidToDefaultDacl(new_token, WinRestrictedCodeSid, GENERIC_ALL)) + return ::GetLastError(); + + if (!AddUserSidToDefaultDacl(new_token, GENERIC_ALL)) + return ::GetLastError(); + + DWORD error = SetTokenIntegrityLevel(new_token, integrity_level_); + if (ERROR_SUCCESS != error) + return error; + + BOOL status = ::DuplicateHandle(::GetCurrentProcess(), + new_token, + ::GetCurrentProcess(), + token_handle, + TOKEN_ALL_ACCESS, + FALSE, // Don't inherit. + 0); + + if (new_token != effective_token_) + ::CloseHandle(new_token); + + if (!status) + return ::GetLastError(); + + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::GetRestrictedTokenHandleForImpersonation( + HANDLE *token_handle) const { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + HANDLE restricted_token_handle; + unsigned err_code = GetRestrictedTokenHandle(&restricted_token_handle); + if (ERROR_SUCCESS != err_code) + return err_code; + + HANDLE impersonation_token; + if (!::DuplicateToken(restricted_token_handle, + SecurityImpersonation, + &impersonation_token)) { + ::CloseHandle(restricted_token_handle); + return ::GetLastError(); + } + + ::CloseHandle(restricted_token_handle); + + BOOL status = ::DuplicateHandle(::GetCurrentProcess(), + impersonation_token, + ::GetCurrentProcess(), + token_handle, + TOKEN_ALL_ACCESS, + FALSE, // Don't inherit. + 0); + + ::CloseHandle(impersonation_token); + + if (!status) + return ::GetLastError(); + + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::AddAllSidsForDenyOnly(std::vector *exceptions) { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + TOKEN_GROUPS *token_groups = NULL; + DWORD size = 0; + + BOOL result = ::GetTokenInformation(effective_token_, + TokenGroups, + NULL, // No buffer. + 0, // Size is 0. + &size); + if (!size) + return ::GetLastError(); + + token_groups = reinterpret_cast(new BYTE[size]); + result = ::GetTokenInformation(effective_token_, + TokenGroups, + token_groups, + size, + &size); + if (!result) { + delete[] reinterpret_cast(token_groups); + return ::GetLastError(); + } + + // Build the list of the deny only group SIDs + for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { + if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 && + (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) { + bool should_ignore = false; + if (exceptions) { + for (unsigned int j = 0; j < exceptions->size(); ++j) { + if (::EqualSid(const_cast((*exceptions)[j].GetPSID()), + token_groups->Groups[i].Sid)) { + should_ignore = true; + break; + } + } + } + if (!should_ignore) { + sids_for_deny_only_.push_back( + reinterpret_cast(token_groups->Groups[i].Sid)); + } + } + } + + delete[] reinterpret_cast(token_groups); + + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::AddSidForDenyOnly(const Sid &sid) { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + sids_for_deny_only_.push_back(sid); + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::AddUserSidForDenyOnly() { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; + TOKEN_USER* token_user = reinterpret_cast(new BYTE[size]); + + BOOL result = ::GetTokenInformation(effective_token_, + TokenUser, + token_user, + size, + &size); + + Sid user = reinterpret_cast(token_user->User.Sid); + delete[] reinterpret_cast(token_user); + + if (!result) + return ::GetLastError(); + + sids_for_deny_only_.push_back(user); + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::DeleteAllPrivileges( + const std::vector *exceptions) { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + // Get the list of privileges in the token + TOKEN_PRIVILEGES *token_privileges = NULL; + DWORD size = 0; + + BOOL result = ::GetTokenInformation(effective_token_, + TokenPrivileges, + NULL, // No buffer. + 0, // Size is 0. + &size); + if (!size) + return ::GetLastError(); + + token_privileges = reinterpret_cast(new BYTE[size]); + result = ::GetTokenInformation(effective_token_, + TokenPrivileges, + token_privileges, + size, + &size); + if (!result) { + delete[] reinterpret_cast(token_privileges); + return ::GetLastError(); + } + + + // Build the list of privileges to disable + for (unsigned int i = 0; i < token_privileges->PrivilegeCount; ++i) { + bool should_ignore = false; + if (exceptions) { + for (unsigned int j = 0; j < exceptions->size(); ++j) { + LUID luid = {0}; + ::LookupPrivilegeValue(NULL, (*exceptions)[j].c_str(), &luid); + if (token_privileges->Privileges[i].Luid.HighPart == luid.HighPart && + token_privileges->Privileges[i].Luid.LowPart == luid.LowPart) { + should_ignore = true; + break; + } + } + } + if (!should_ignore) { + privileges_to_disable_.push_back(token_privileges->Privileges[i].Luid); + } + } + + delete[] reinterpret_cast(token_privileges); + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::DeletePrivilege(const wchar_t *privilege) { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + LUID luid = {0}; + if (LookupPrivilegeValue(NULL, privilege, &luid)) + privileges_to_disable_.push_back(luid); + else + return ::GetLastError(); + + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::AddRestrictingSid(const Sid &sid) { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + sids_to_restrict_.push_back(sid); // No attributes + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::AddRestrictingSidLogonSession() { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + TOKEN_GROUPS *token_groups = NULL; + DWORD size = 0; + + BOOL result = ::GetTokenInformation(effective_token_, + TokenGroups, + NULL, // No buffer. + 0, // Size is 0. + &size); + if (!size) + return ::GetLastError(); + + token_groups = reinterpret_cast(new BYTE[size]); + result = ::GetTokenInformation(effective_token_, + TokenGroups, + token_groups, + size, + &size); + if (!result) { + delete[] reinterpret_cast(token_groups); + return ::GetLastError(); + } + + SID *logon_sid = NULL; + for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { + if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) { + logon_sid = static_cast(token_groups->Groups[i].Sid); + break; + } + } + + if (logon_sid) + sids_to_restrict_.push_back(logon_sid); + + delete[] reinterpret_cast(token_groups); + + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::AddRestrictingSidCurrentUser() { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; + TOKEN_USER* token_user = reinterpret_cast(new BYTE[size]); + + BOOL result = ::GetTokenInformation(effective_token_, + TokenUser, + token_user, + size, + &size); + + Sid user = reinterpret_cast(token_user->User.Sid); + delete[] reinterpret_cast(token_user); + + + if (!result) + return ::GetLastError(); + + sids_to_restrict_.push_back(user); + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::AddRestrictingSidAllSids() { + DCHECK(init_); + if (!init_) + return ERROR_NO_TOKEN; + + // Add the current user to the list. + unsigned error = AddRestrictingSidCurrentUser(); + if (ERROR_SUCCESS != error) + return error; + + TOKEN_GROUPS *token_groups = NULL; + DWORD size = 0; + + // Get the buffer size required. + BOOL result = ::GetTokenInformation(effective_token_, TokenGroups, NULL, 0, + &size); + if (!size) + return ::GetLastError(); + + token_groups = reinterpret_cast(new BYTE[size]); + result = ::GetTokenInformation(effective_token_, + TokenGroups, + token_groups, + size, + &size); + if (!result) { + delete[] reinterpret_cast(token_groups); + return ::GetLastError(); + } + + // Build the list of restricting sids from all groups. + for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { + if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0) + AddRestrictingSid(reinterpret_cast(token_groups->Groups[i].Sid)); + } + + delete[] reinterpret_cast(token_groups); + + return ERROR_SUCCESS; +} + +unsigned RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) { + integrity_level_ = integrity_level; + return ERROR_SUCCESS; +} + +} // namespace sandbox diff --git a/sandbox/win/src/restricted_token.h b/sandbox/win/src/restricted_token.h new file mode 100644 index 0000000..88bd70f --- /dev/null +++ b/sandbox/win/src/restricted_token.h @@ -0,0 +1,198 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_RESTRICTED_TOKEN_H_ +#define SANDBOX_SRC_RESTRICTED_TOKEN_H_ + +#include +#include + +#include "base/basictypes.h" +#include "sandbox/src/restricted_token_utils.h" +#include "sandbox/src/security_level.h" +#include "sandbox/src/sid.h" + +// Flags present in the Group SID list. These 2 flags are new in Windows Vista +#ifndef SE_GROUP_INTEGRITY +#define SE_GROUP_INTEGRITY (0x00000020L) +#endif +#ifndef SE_GROUP_INTEGRITY_ENABLED +#define SE_GROUP_INTEGRITY_ENABLED (0x00000040L) +#endif + +namespace sandbox { + +// Handles the creation of a restricted token using the effective token or +// any token handle. +// Sample usage: +// RestrictedToken restricted_token; +// unsigned err_code = restricted_token.Init(NULL); // Use the current +// // effective token +// if (ERROR_SUCCESS != err_code) { +// // handle error. +// } +// +// restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID()); +// HANDLE token_handle; +// err_code = restricted_token.GetRestrictedTokenHandle(&token_handle); +// if (ERROR_SUCCESS != err_code) { +// // handle error. +// } +// [...] +// CloseHandle(token_handle); +class RestrictedToken { + public: + // Init() has to be called before calling any other method in the class. + RestrictedToken() + : init_(false), effective_token_(NULL), + integrity_level_(INTEGRITY_LEVEL_LAST) { } + + ~RestrictedToken() { + if (effective_token_) + CloseHandle(effective_token_); + } + + // Initializes the RestrictedToken object with effective_token. + // If effective_token is NULL, it initializes the RestrictedToken object with + // the effective token of the current process. + unsigned Init(HANDLE effective_token); + + // Creates a restricted token and returns its handle using the token_handle + // output parameter. This handle has to be closed by the caller. + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + unsigned GetRestrictedTokenHandle(HANDLE *token_handle) const; + + // Creates a restricted token and uses this new token to create a new token + // for impersonation. Returns the handle of this impersonation token using + // the token_handle output parameter. This handle has to be closed by + // the caller. + // + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + // + // The sample usage is the same as the GetRestrictedTokenHandle function. + unsigned GetRestrictedTokenHandleForImpersonation(HANDLE *token_handle) const; + + // Lists all sids in the token and mark them as Deny Only except for those + // present in the exceptions parameter. If there is no exception needed, + // the caller can pass an empty list or NULL for the exceptions + // parameter. + // + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + // + // Sample usage: + // std::vector sid_exceptions; + // sid_exceptions.push_back(ATL::Sids::Users().GetPSID()); + // sid_exceptions.push_back(ATL::Sids::World().GetPSID()); + // restricted_token.AddAllSidsForDenyOnly(&sid_exceptions); + // Note: A Sid marked for Deny Only in a token cannot be used to grant + // access to any resource. It can only be used to deny access. + unsigned AddAllSidsForDenyOnly(std::vector *exceptions); + + // Adds a user or group SID for Deny Only in the restricted token. + // Parameter: sid is the SID to add in the Deny Only list. + // The return value is always ERROR_SUCCESS. + // + // Sample Usage: + // restricted_token.AddSidForDenyOnly(ATL::Sids::Admins().GetPSID()); + unsigned AddSidForDenyOnly(const Sid &sid); + + // Adds the user sid of the token for Deny Only in the restricted token. + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + unsigned AddUserSidForDenyOnly(); + + // Lists all privileges in the token and add them to the list of privileges + // to remove except for those present in the exceptions parameter. If + // there is no exception needed, the caller can pass an empty list or NULL + // for the exceptions parameter. + // + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + // + // Sample usage: + // std::vector privilege_exceptions; + // privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + // restricted_token.DeleteAllPrivileges(&privilege_exceptions); + unsigned DeleteAllPrivileges( + const std::vector *exceptions); + + // Adds a privilege to the list of privileges to remove in the restricted + // token. + // Parameter: privilege is the privilege name to remove. This is the string + // representing the privilege. (e.g. "SeChangeNotifyPrivilege"). + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + // + // Sample usage: + // restricted_token.DeletePrivilege(SE_LOAD_DRIVER_NAME); + unsigned DeletePrivilege(const wchar_t *privilege); + + // Adds a SID to the list of restricting sids in the restricted token. + // Parameter: sid is the sid to add to the list restricting sids. + // The return value is always ERROR_SUCCESS. + // + // Sample usage: + // restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID()); + // Note: The list of restricting is used to force Windows to perform all + // access checks twice. The first time using your user SID and your groups, + // and the second time using your list of restricting sids. The access has + // to be granted in both places to get access to the resource requested. + unsigned AddRestrictingSid(const Sid &sid); + + // Adds the logon sid of the token in the list of restricting sids for the + // restricted token. + // + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + unsigned AddRestrictingSidLogonSession(); + + // Adds the owner sid of the token in the list of restricting sids for the + // restricted token. + // + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + unsigned AddRestrictingSidCurrentUser(); + + // Adds all group sids and the user sid to the restricting sids list. + // + // If the function succeeds, the return value is ERROR_SUCCESS. If the + // function fails, the return value is the win32 error code corresponding to + // the error. + unsigned AddRestrictingSidAllSids(); + + // Sets the token integrity level. This is only valid on Vista. The integrity + // level cannot be higher than your current integrity level. + unsigned SetIntegrityLevel(IntegrityLevel integrity_level); + + private: + // The list of restricting sids in the restricted token. + std::vector sids_to_restrict_; + // The list of privileges to remove in the restricted token. + std::vector privileges_to_disable_; + // The list of sids to mark as Deny Only in the restricted token. + std::vector sids_for_deny_only_; + // The token to restrict. Can only be set in a constructor. + HANDLE effective_token_; + // The token integrity level. Only valid on Vista. + IntegrityLevel integrity_level_; + // Tells if the object is initialized or not (if Init() has been called) + bool init_; + + DISALLOW_COPY_AND_ASSIGN(RestrictedToken); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_RESTRICTED_TOKEN_H_ diff --git a/sandbox/win/src/restricted_token_unittest.cc b/sandbox/win/src/restricted_token_unittest.cc new file mode 100644 index 0000000..310b73f --- /dev/null +++ b/sandbox/win/src/restricted_token_unittest.cc @@ -0,0 +1,530 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains unit tests for the RestrictedToken. + +#define _ATL_NO_EXCEPTIONS +#include +#include +#include +#include "sandbox/src/restricted_token.h" +#include "sandbox/src/sid.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +// Tests the initializatioin with an invalid token handle. +TEST(RestrictedTokenTest, InvalidHandle) { + RestrictedToken token; + ASSERT_EQ(ERROR_INVALID_HANDLE, token.Init(reinterpret_cast(0x5555))); +} + +// Tests the initialization with NULL as parameter. +TEST(RestrictedTokenTest, DefaultInit) { + // Get the current process token. + HANDLE token_handle = INVALID_HANDLE_VALUE; + ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, + &token_handle)); + + ASSERT_NE(INVALID_HANDLE_VALUE, token_handle); + + ATL::CAccessToken access_token; + access_token.Attach(token_handle); + + // Create the token using the current token. + RestrictedToken token_default; + ASSERT_EQ(ERROR_SUCCESS, token_default.Init(NULL)); + + // Get the handle to the restricted token. + + HANDLE restricted_token_handle = NULL; + ASSERT_EQ(ERROR_SUCCESS, + token_default.GetRestrictedTokenHandle(&restricted_token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(restricted_token_handle); + + ATL::CSid sid_user_restricted; + ATL::CSid sid_user_default; + ATL::CSid sid_owner_restricted; + ATL::CSid sid_owner_default; + ASSERT_TRUE(restricted_token.GetUser(&sid_user_restricted)); + ASSERT_TRUE(access_token.GetUser(&sid_user_default)); + ASSERT_TRUE(restricted_token.GetOwner(&sid_owner_restricted)); + ASSERT_TRUE(access_token.GetOwner(&sid_owner_default)); + + // Check if both token have the same owner and user. + ASSERT_EQ(sid_user_restricted, sid_user_default); + ASSERT_EQ(sid_owner_restricted, sid_owner_default); +} + +// Tests the initialization with a custom token as parameter. +TEST(RestrictedTokenTest, CustomInit) { + // Get the current process token. + HANDLE token_handle = INVALID_HANDLE_VALUE; + ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, + &token_handle)); + + ASSERT_NE(INVALID_HANDLE_VALUE, token_handle); + + ATL::CAccessToken access_token; + access_token.Attach(token_handle); + + // Change the primary group. + access_token.SetPrimaryGroup(ATL::Sids::World()); + + // Create the token using the current token. + RestrictedToken token; + ASSERT_EQ(ERROR_SUCCESS, token.Init(access_token.GetHandle())); + + // Get the handle to the restricted token. + + HANDLE restricted_token_handle = NULL; + ASSERT_EQ(ERROR_SUCCESS, + token.GetRestrictedTokenHandle(&restricted_token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(restricted_token_handle); + + ATL::CSid sid_restricted; + ATL::CSid sid_default; + ASSERT_TRUE(restricted_token.GetPrimaryGroup(&sid_restricted)); + ASSERT_TRUE(access_token.GetPrimaryGroup(&sid_default)); + + // Check if both token have the same owner. + ASSERT_EQ(sid_restricted, sid_default); +} + +// Verifies that the token created by the object are valid. +TEST(RestrictedTokenTest, ResultToken) { + RestrictedToken token; + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + + ASSERT_EQ(ERROR_SUCCESS, + token.AddRestrictingSid(ATL::Sids::World().GetPSID())); + + HANDLE restricted_token; + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&restricted_token)); + + ASSERT_TRUE(::IsTokenRestricted(restricted_token)); + + DWORD length = 0; + TOKEN_TYPE type; + ASSERT_TRUE(::GetTokenInformation(restricted_token, + ::TokenType, + &type, + sizeof(type), + &length)); + + ASSERT_EQ(type, TokenPrimary); + + HANDLE impersonation_token; + ASSERT_EQ(ERROR_SUCCESS, + token.GetRestrictedTokenHandleForImpersonation(&impersonation_token)); + + ASSERT_TRUE(::IsTokenRestricted(impersonation_token)); + + ASSERT_TRUE(::GetTokenInformation(impersonation_token, + ::TokenType, + &type, + sizeof(type), + &length)); + + ASSERT_EQ(type, TokenImpersonation); + + ::CloseHandle(impersonation_token); + ::CloseHandle(restricted_token); +} + +// Verifies that the token created has "Restricted" in its default dacl. +TEST(RestrictedTokenTest, DefaultDacl) { + RestrictedToken token; + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + + ASSERT_EQ(ERROR_SUCCESS, + token.AddRestrictingSid(ATL::Sids::World().GetPSID())); + + HANDLE handle; + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(handle); + + ATL::CDacl dacl; + ASSERT_TRUE(restricted_token.GetDefaultDacl(&dacl)); + + bool restricted_found = false; + + unsigned int ace_count = dacl.GetAceCount(); + for (unsigned int i = 0; i < ace_count ; ++i) { + ATL::CSid sid; + ACCESS_MASK mask = 0; + dacl.GetAclEntry(i, &sid, &mask); + if (sid == ATL::Sids::RestrictedCode() && mask == GENERIC_ALL) { + restricted_found = true; + break; + } + } + + ASSERT_TRUE(restricted_found); +} + +// Tests the method "AddSidForDenyOnly". +TEST(RestrictedTokenTest, DenySid) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.AddSidForDenyOnly(Sid(WinWorldSid))); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + + ATL::CTokenGroups groups; + ASSERT_TRUE(restricted_token.GetGroups(&groups)); + + ATL::CSid::CSidArray sids; + ATL::CAtlArray attributes; + groups.GetSidsAndAttributes(&sids, &attributes); + + for (unsigned int i = 0; i < sids.GetCount(); i++) { + if (ATL::Sids::World() == sids[i]) { + ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, + attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); + } + } +} + +// Tests the method "AddAllSidsForDenyOnly". +TEST(RestrictedTokenTest, DenySids) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.AddAllSidsForDenyOnly(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + + ATL::CTokenGroups groups; + ASSERT_TRUE(restricted_token.GetGroups(&groups)); + + ATL::CSid::CSidArray sids; + ATL::CAtlArray attributes; + groups.GetSidsAndAttributes(&sids, &attributes); + + // Verify that all sids are really gone. + for (unsigned int i = 0; i < sids.GetCount(); i++) { + if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 && + (attributes[i] & SE_GROUP_INTEGRITY) == 0) { + ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, + attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); + } + } +} + +// Tests the method "AddAllSidsForDenyOnly" using an exception list. +TEST(RestrictedTokenTest, DenySidsException) { + RestrictedToken token; + HANDLE token_handle = NULL; + + std::vector sids_exception; + sids_exception.push_back(Sid(WinWorldSid)); + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.AddAllSidsForDenyOnly(&sids_exception)); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + + ATL::CTokenGroups groups; + ASSERT_TRUE(restricted_token.GetGroups(&groups)); + + ATL::CSid::CSidArray sids; + ATL::CAtlArray attributes; + groups.GetSidsAndAttributes(&sids, &attributes); + + // Verify that all sids are really gone. + for (unsigned int i = 0; i < sids.GetCount(); i++) { + if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 && + (attributes[i] & SE_GROUP_INTEGRITY) == 0) { + if (ATL::Sids::World() == sids[i]) { + ASSERT_EQ(NULL, attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); + } else { + ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, + attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); + } + } + } +} + +// Tests test method AddOwnerSidForDenyOnly. +TEST(RestrictedTokenTest, DenyOwnerSid) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.AddUserSidForDenyOnly()); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + + ATL::CTokenGroups groups; + ASSERT_TRUE(restricted_token.GetGroups(&groups)); + + ATL::CSid::CSidArray sids; + ATL::CAtlArray attributes; + groups.GetSidsAndAttributes(&sids, &attributes); + + ATL::CSid user_sid; + ASSERT_TRUE(restricted_token.GetUser(&user_sid)); + + for (unsigned int i = 0; i < sids.GetCount(); ++i) { + if (user_sid == sids[i]) { + ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, + attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); + } + } +} + +// Tests the method DeleteAllPrivileges. +TEST(RestrictedTokenTest, DeleteAllPrivileges) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.DeleteAllPrivileges(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + + ATL::CTokenPrivileges privileges; + ASSERT_TRUE(restricted_token.GetPrivileges(&privileges)); + + ASSERT_EQ(0, privileges.GetCount()); +} + +// Tests the method DeleteAllPrivileges with an exception list. +TEST(RestrictedTokenTest, DeleteAllPrivilegesException) { + RestrictedToken token; + HANDLE token_handle = NULL; + + std::vector exceptions; + exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.DeleteAllPrivileges(&exceptions)); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + + ATL::CTokenPrivileges privileges; + ASSERT_TRUE(restricted_token.GetPrivileges(&privileges)); + + ATL::CTokenPrivileges::CNames privilege_names; + ATL::CTokenPrivileges::CAttributes privilege_name_attributes; + privileges.GetNamesAndAttributes(&privilege_names, + &privilege_name_attributes); + + ASSERT_EQ(1, privileges.GetCount()); + + for (unsigned int i = 0; i < privileges.GetCount(); ++i) { + ASSERT_EQ(privilege_names[i], SE_CHANGE_NOTIFY_NAME); + } +} + +// Tests the method DeletePrivilege. +TEST(RestrictedTokenTest, DeletePrivilege) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.DeletePrivilege(SE_CHANGE_NOTIFY_NAME)); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + + ATL::CTokenPrivileges privileges; + ASSERT_TRUE(restricted_token.GetPrivileges(&privileges)); + + ATL::CTokenPrivileges::CNames privilege_names; + ATL::CTokenPrivileges::CAttributes privilege_name_attributes; + privileges.GetNamesAndAttributes(&privilege_names, + &privilege_name_attributes); + + for (unsigned int i = 0; i < privileges.GetCount(); ++i) { + ASSERT_NE(privilege_names[i], SE_CHANGE_NOTIFY_NAME); + } +} + +// Checks if a sid is in the restricting list of the restricted token. +// Asserts if it's not the case. If count is a positive number, the number of +// elements in the restricting sids list has to be equal. +void CheckRestrictingSid(const ATL::CAccessToken &restricted_token, + ATL::CSid sid, int count) { + DWORD length = 1000; + BYTE *memory = new BYTE[1000]; + TOKEN_GROUPS *groups = reinterpret_cast(memory); + ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(), + TokenRestrictedSids, + groups, + length, + &length)); + + ATL::CTokenGroups atl_groups(*groups); + delete[] memory; + + if (count >= 0) + ASSERT_EQ(count, atl_groups.GetCount()); + + ATL::CSid::CSidArray sids; + ATL::CAtlArray attributes; + atl_groups.GetSidsAndAttributes(&sids, &attributes); + + bool present = false; + for (unsigned int i = 0; i < sids.GetCount(); ++i) { + if (sids[i] == sid) { + present = true; + break; + } + } + + ASSERT_TRUE(present); +} + +// Tests the method AddRestrictingSid. +TEST(RestrictedTokenTest, AddRestrictingSid) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, + token.AddRestrictingSid(ATL::Sids::World().GetPSID())); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + + CheckRestrictingSid(restricted_token, ATL::Sids::World(), 1); +} + +// Tests the method AddRestrictingSidCurrentUser. +TEST(RestrictedTokenTest, AddRestrictingSidCurrentUser) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidCurrentUser()); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + ATL::CSid user; + restricted_token.GetUser(&user); + + CheckRestrictingSid(restricted_token, user, 1); +} + +// Tests the method AddRestrictingSidLogonSession. +TEST(RestrictedTokenTest, AddRestrictingSidLogonSession) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidLogonSession()); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + ATL::CSid session; + restricted_token.GetLogonSid(&session); + + CheckRestrictingSid(restricted_token, session, 1); +} + +// Tests adding a lot of restricting sids. +TEST(RestrictedTokenTest, AddMultipleRestrictingSids) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidCurrentUser()); + ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidLogonSession()); + ASSERT_EQ(ERROR_SUCCESS, + token.AddRestrictingSid(ATL::Sids::World().GetPSID())); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + ATL::CSid session; + restricted_token.GetLogonSid(&session); + + DWORD length = 1000; + BYTE *memory = new BYTE[1000]; + TOKEN_GROUPS *groups = reinterpret_cast(memory); + ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(), + TokenRestrictedSids, + groups, + length, + &length)); + + ATL::CTokenGroups atl_groups(*groups); + delete[] memory; + + ASSERT_EQ(3, atl_groups.GetCount()); +} + +// Tests the method "AddRestrictingSidAllSids". +TEST(RestrictedTokenTest, AddAllSidToRestrictingSids) { + RestrictedToken token; + HANDLE token_handle = NULL; + + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidAllSids()); + ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + + ATL::CAccessToken restricted_token; + restricted_token.Attach(token_handle); + + ATL::CTokenGroups groups; + ASSERT_TRUE(restricted_token.GetGroups(&groups)); + + ATL::CSid::CSidArray sids; + ATL::CAtlArray attributes; + groups.GetSidsAndAttributes(&sids, &attributes); + + // Verify that all group sids are in the restricting sid list. + for (unsigned int i = 0; i < sids.GetCount(); i++) { + if ((attributes[i] & SE_GROUP_INTEGRITY) == 0) { + CheckRestrictingSid(restricted_token, sids[i], -1); + } + } + + // Verify that the user is in the restricting sid list. + ATL::CSid user; + restricted_token.GetUser(&user); + CheckRestrictingSid(restricted_token, user, -1); +} + +// Test to be executed only in release because they are triggering DCHECKs. +#ifndef _DEBUG + +// Checks the error code when the object is initialized twice. +TEST(RestrictedTokenTest, DoubleInit) { + RestrictedToken token; + ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + + ASSERT_EQ(ERROR_ALREADY_INITIALIZED, token.Init(NULL)); +} + +#endif + +} // namespace sandbox diff --git a/sandbox/win/src/restricted_token_utils.cc b/sandbox/win/src/restricted_token_utils.cc new file mode 100644 index 0000000..df30b40 --- /dev/null +++ b/sandbox/win/src/restricted_token_utils.cc @@ -0,0 +1,344 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +#include "sandbox/src/restricted_token_utils.h" + +#include "base/logging.h" +#include "base/win/scoped_handle.h" +#include "base/win/scoped_process_information.h" +#include "base/win/windows_version.h" +#include "sandbox/src/job.h" +#include "sandbox/src/restricted_token.h" +#include "sandbox/src/security_level.h" +#include "sandbox/src/sid.h" + +namespace sandbox { + +DWORD CreateRestrictedToken(HANDLE *token_handle, + TokenLevel security_level, + IntegrityLevel integrity_level, + TokenType token_type) { + if (!token_handle) + return ERROR_BAD_ARGUMENTS; + + RestrictedToken restricted_token; + restricted_token.Init(NULL); // Initialized with the current process token + + std::vector privilege_exceptions; + std::vector sid_exceptions; + + bool deny_sids = true; + bool remove_privileges = true; + + switch (security_level) { + case USER_UNPROTECTED: { + deny_sids = false; + remove_privileges = false; + break; + } + case USER_RESTRICTED_SAME_ACCESS: { + deny_sids = false; + remove_privileges = false; + + unsigned err_code = restricted_token.AddRestrictingSidAllSids(); + if (ERROR_SUCCESS != err_code) + return err_code; + + break; + } + case USER_NON_ADMIN: { + sid_exceptions.push_back(WinBuiltinUsersSid); + sid_exceptions.push_back(WinWorldSid); + sid_exceptions.push_back(WinInteractiveSid); + sid_exceptions.push_back(WinAuthenticatedUserSid); + privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + break; + } + case USER_INTERACTIVE: { + sid_exceptions.push_back(WinBuiltinUsersSid); + sid_exceptions.push_back(WinWorldSid); + sid_exceptions.push_back(WinInteractiveSid); + sid_exceptions.push_back(WinAuthenticatedUserSid); + privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + restricted_token.AddRestrictingSid(WinBuiltinUsersSid); + restricted_token.AddRestrictingSid(WinWorldSid); + restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + restricted_token.AddRestrictingSidCurrentUser(); + restricted_token.AddRestrictingSidLogonSession(); + break; + } + case USER_LIMITED: { + sid_exceptions.push_back(WinBuiltinUsersSid); + sid_exceptions.push_back(WinWorldSid); + sid_exceptions.push_back(WinInteractiveSid); + privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + restricted_token.AddRestrictingSid(WinBuiltinUsersSid); + restricted_token.AddRestrictingSid(WinWorldSid); + restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + + // This token has to be able to create objects in BNO. + // Unfortunately, on vista, it needs the current logon sid + // in the token to achieve this. You should also set the process to be + // low integrity level so it can't access object created by other + // processes. + if (base::win::GetVersion() >= base::win::VERSION_VISTA) + restricted_token.AddRestrictingSidLogonSession(); + break; + } + case USER_RESTRICTED: { + privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + restricted_token.AddUserSidForDenyOnly(); + restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + break; + } + case USER_LOCKDOWN: { + restricted_token.AddUserSidForDenyOnly(); + restricted_token.AddRestrictingSid(WinNullSid); + break; + } + default: { + return ERROR_BAD_ARGUMENTS; + } + } + + DWORD err_code = ERROR_SUCCESS; + if (deny_sids) { + err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions); + if (ERROR_SUCCESS != err_code) + return err_code; + } + + if (remove_privileges) { + err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions); + if (ERROR_SUCCESS != err_code) + return err_code; + } + + restricted_token.SetIntegrityLevel(integrity_level); + + switch (token_type) { + case PRIMARY: { + err_code = restricted_token.GetRestrictedTokenHandle(token_handle); + break; + } + case IMPERSONATION: { + err_code = restricted_token.GetRestrictedTokenHandleForImpersonation( + token_handle); + break; + } + default: { + err_code = ERROR_BAD_ARGUMENTS; + break; + } + } + + return err_code; +} + +DWORD StartRestrictedProcessInJob(wchar_t *command_line, + TokenLevel primary_level, + TokenLevel impersonation_level, + JobLevel job_level, + HANDLE *const job_handle_ret) { + Job job; + DWORD err_code = job.Init(job_level, NULL, 0); + if (ERROR_SUCCESS != err_code) + return err_code; + + if (JOB_UNPROTECTED != job_level) { + // Share the Desktop handle to be able to use MessageBox() in the sandboxed + // application. + err_code = job.UserHandleGrantAccess(GetDesktopWindow()); + if (ERROR_SUCCESS != err_code) + return err_code; + } + + // Create the primary (restricted) token for the process + HANDLE primary_token_handle = NULL; + err_code = CreateRestrictedToken(&primary_token_handle, + primary_level, + INTEGRITY_LEVEL_LAST, + PRIMARY); + if (ERROR_SUCCESS != err_code) { + return err_code; + } + base::win::ScopedHandle primary_token(primary_token_handle); + + // Create the impersonation token (restricted) to be able to start the + // process. + HANDLE impersonation_token_handle; + err_code = CreateRestrictedToken(&impersonation_token_handle, + impersonation_level, + INTEGRITY_LEVEL_LAST, + IMPERSONATION); + if (ERROR_SUCCESS != err_code) { + return err_code; + } + base::win::ScopedHandle impersonation_token(impersonation_token_handle); + + // Start the process + STARTUPINFO startup_info = {0}; + base::win::ScopedProcessInformation process_info; + DWORD flags = CREATE_SUSPENDED; + + if (base::win::GetVersion() < base::win::VERSION_WIN8) { + // Windows 8 implements nested jobs, but for older systems we need to + // break out of any job we're in to enforce our restrictions. + flags |= CREATE_BREAKAWAY_FROM_JOB; + } + + if (!::CreateProcessAsUser(primary_token.Get(), + NULL, // No application name. + command_line, + NULL, // No security attribute. + NULL, // No thread attribute. + FALSE, // Do not inherit handles. + flags, + NULL, // Use the environment of the caller. + NULL, // Use current directory of the caller. + &startup_info, + process_info.Receive())) { + return ::GetLastError(); + } + + // Change the token of the main thread of the new process for the + // impersonation token with more rights. + { + HANDLE temp_thread = process_info.thread_handle(); + if (!::SetThreadToken(&temp_thread, impersonation_token.Get())) { + ::TerminateProcess(process_info.process_handle(), + 0); // exit code + return ::GetLastError(); + } + } + + err_code = job.AssignProcessToJob(process_info.process_handle()); + if (ERROR_SUCCESS != err_code) { + ::TerminateProcess(process_info.process_handle(), + 0); // exit code + return ::GetLastError(); + } + + // Start the application + ::ResumeThread(process_info.thread_handle()); + + (*job_handle_ret) = job.Detach(); + + return ERROR_SUCCESS; +} + +DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type, + const wchar_t* ace_access, + const wchar_t* integrity_level_sid) { + // Build the SDDL string for the label. + std::wstring sddl = L"S:("; // SDDL for a SACL. + sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label". + sddl += L";;"; // No Ace Flags. + sddl += ace_access; // Add the ACE access. + sddl += L";;;"; // No ObjectType and Inherited Object Type. + sddl += integrity_level_sid; // Trustee Sid. + sddl += L")"; + + DWORD error = ERROR_SUCCESS; + PSECURITY_DESCRIPTOR sec_desc = NULL; + + PACL sacl = NULL; + BOOL sacl_present = FALSE; + BOOL sacl_defaulted = FALSE; + + if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(), + SDDL_REVISION, + &sec_desc, NULL)) { + if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl, + &sacl_defaulted)) { + error = ::SetSecurityInfo(handle, type, + LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, + sacl); + } else { + error = ::GetLastError(); + } + + ::LocalFree(sec_desc); + } else { + return::GetLastError(); + } + + return error; +} + +const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) { + switch (integrity_level) { + case INTEGRITY_LEVEL_SYSTEM: + return L"S-1-16-16384"; + case INTEGRITY_LEVEL_HIGH: + return L"S-1-16-12288"; + case INTEGRITY_LEVEL_MEDIUM: + return L"S-1-16-8192"; + case INTEGRITY_LEVEL_MEDIUM_LOW: + return L"S-1-16-6144"; + case INTEGRITY_LEVEL_LOW: + return L"S-1-16-4096"; + case INTEGRITY_LEVEL_BELOW_LOW: + return L"S-1-16-2048"; + case INTEGRITY_LEVEL_UNTRUSTED: + return L"S-1-16-0"; + case INTEGRITY_LEVEL_LAST: + return NULL; + } + + NOTREACHED(); + return NULL; +} +DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) { + if (base::win::GetVersion() < base::win::VERSION_VISTA) + return ERROR_SUCCESS; + + const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level); + if (!integrity_level_str) { + // No mandatory level specified, we don't change it. + return ERROR_SUCCESS; + } + + PSID integrity_sid = NULL; + if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid)) + return ::GetLastError(); + + TOKEN_MANDATORY_LABEL label = {0}; + label.Label.Attributes = SE_GROUP_INTEGRITY; + label.Label.Sid = integrity_sid; + + DWORD size = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(integrity_sid); + BOOL result = ::SetTokenInformation(token, TokenIntegrityLevel, &label, + size); + ::LocalFree(integrity_sid); + + return result ? ERROR_SUCCESS : ::GetLastError(); +} + +DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) { + if (base::win::GetVersion() < base::win::VERSION_VISTA) + return ERROR_SUCCESS; + + // We don't check for an invalid level here because we'll just let it + // fail on the SetTokenIntegrityLevel call later on. + if (integrity_level == INTEGRITY_LEVEL_LAST) { + // No mandatory level specified, we don't change it. + return ERROR_SUCCESS; + } + + HANDLE token_handle; + if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT, + &token_handle)) + return ::GetLastError(); + + base::win::ScopedHandle token(token_handle); + + return SetTokenIntegrityLevel(token.Get(), integrity_level); +} + +} // namespace sandbox diff --git a/sandbox/win/src/restricted_token_utils.h b/sandbox/win/src/restricted_token_utils.h new file mode 100644 index 0000000..0aade8b --- /dev/null +++ b/sandbox/win/src/restricted_token_utils.h @@ -0,0 +1,83 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__ +#define SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__ + +#include +#include + +#include "sandbox/src/restricted_token.h" +#include "sandbox/src/security_level.h" + +// Contains the utility functions to be able to create restricted tokens based +// on a security profiles. + +namespace sandbox { + +// The type of the token returned by the CreateNakedToken. +enum TokenType { + IMPERSONATION = 0, + PRIMARY +}; + +// Creates a restricted token based on the effective token of the current +// process. The parameter security_level determines how much the token is +// restricted. The token_type determines if the token will be used as a primary +// token or impersonation token. The integrity level of the token is set to +// |integrity level| on Vista only. +// token_handle is the output value containing the handle of the +// newly created restricted token. +// If the function succeeds, the return value is ERROR_SUCCESS. If the +// function fails, the return value is the win32 error code corresponding to +// the error. +DWORD CreateRestrictedToken(HANDLE *token_handle, + TokenLevel security_level, + IntegrityLevel integrity_level, + TokenType token_type); + +// Starts the process described by the input parameter command_line in a job +// with a restricted token. Also set the main thread of this newly created +// process to impersonate a user with more rights so it can initialize +// correctly. +// +// Parameters: primary_level is the security level of the primary token. +// impersonation_level is the security level of the impersonation token used +// to initialize the process. job_level is the security level of the job +// object used to encapsulate the process. +// +// The output parameter job_handle is the handle to the job object. It has +// to be closed with CloseHandle() when not needed. Closing this handle will +// kill the process started. +// +// Note: The process started with this function has to call RevertToSelf() as +// soon as possible to stop using the impersonation token and start being +// secure. +// +// Note: The Unicode version of this function will fail if the command_line +// parameter is a const string. +DWORD StartRestrictedProcessInJob(wchar_t *command_line, + TokenLevel primary_level, + TokenLevel impersonation_level, + JobLevel job_level, + HANDLE *job_handle); + +// Sets the integrity label on a object handle. +DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type, + const wchar_t* ace_access, + const wchar_t* integrity_level_sid); + +// Sets the integrity level on a token. This is only valid on Vista. It returns +// without failing on XP. If the integrity level that you specify is greater +// than the current integrity level, the function will fail. +DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level); + +// Sets the integrity level on the current process on Vista. It returns without +// failing on XP. If the integrity level that you specify is greater than the +// current integrity level, the function will fail. +DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level); + +} // namespace sandbox + +#endif // SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__ diff --git a/sandbox/win/src/sandbox.cc b/sandbox/win/src/sandbox.cc new file mode 100644 index 0000000..f70c702 --- /dev/null +++ b/sandbox/win/src/sandbox.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/broker_services.h" +#include "sandbox/src/target_services.h" + +#if defined(_WIN64) && !defined(NACL_WIN64) +// We allow building this code for Win64 as part of NaCl to enable development +#error Sandbox code was not tested on 64-bit Windows. See \ + http://crbug.com/27218 for details and progress log. +#endif + + +namespace sandbox { +// The section for IPC and policy. +SANDBOX_INTERCEPT HANDLE g_shared_section = NULL; + +static bool s_is_broker = false; + +// GetBrokerServices: the current implementation relies on a shared section +// that is created by the broker and opened by the target. +BrokerServices* SandboxFactory::GetBrokerServices() { + // Can't be the broker if the shared section is open. + if (NULL != g_shared_section) { + return NULL; + } + // If the shared section does not exist we are the broker, then create + // the broker object. + s_is_broker = true; + return BrokerServicesBase::GetInstance(); +} + +// GetTargetServices implementation must follow the same technique as the +// GetBrokerServices, but in this case the logic is the opposite. +TargetServices* SandboxFactory::GetTargetServices() { + // Can't be the target if the section handle is not valid. + if (NULL == g_shared_section) { + return NULL; + } + // We are the target + s_is_broker = false; + // Creates and returns the target services implementation. + return TargetServicesBase::GetInstance(); +} + +} // namespace sandbox diff --git a/sandbox/win/src/sandbox.h b/sandbox/win/src/sandbox.h new file mode 100644 index 0000000..18c05ce --- /dev/null +++ b/sandbox/win/src/sandbox.h @@ -0,0 +1,156 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Sandbox is a sandbox library for windows processes. Use when you want a +// 'privileged' process and a 'locked down process' to interact with. +// The privileged process is called the broker and it is started by external +// means (such as the user starting it). The 'sandboxed' process is called the +// target and it is started by the broker. There can be many target processes +// started by a single broker process. This library provides facilities +// for both the broker and the target. +// +// The design rationale and relevant documents can be found at http://go/sbox. +// +// Note: this header does not include the SandboxFactory definitions because +// there are cases where the Sandbox library is linked against the main .exe +// while its API needs to be used in a DLL. + +#ifndef SANDBOX_SRC_SANDBOX_H__ +#define SANDBOX_SRC_SANDBOX_H__ + +#include + +#include "base/basictypes.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/sandbox_types.h" + +// sandbox: Google User-Land Application Sandbox +namespace sandbox { + +class BrokerServices; +class ProcessState; +class TargetPolicy; +class TargetServices; + +// BrokerServices exposes all the broker API. +// The basic use is to start the target(s) and wait for them to end. +// +// This API is intended to be called in the following order +// (error checking omitted): +// BrokerServices* broker = SandboxFactory::GetBrokerServices(); +// broker->Init(); +// PROCESS_INFORMATION target; +// broker->SpawnTarget(target_exe_path, target_args, &target); +// ::ResumeThread(target->hThread); +// // -- later you can call: +// broker->WaitForAllTargets(option); +// +class BrokerServices { + public: + // Initializes the broker. Must be called before any other on this class. + // returns ALL_OK if successful. All other return values imply failure. + // If the return is ERROR_GENERIC, you can call ::GetLastError() to get + // more information. + virtual ResultCode Init() = 0; + + // Returns the interface pointer to a new, empty policy object. Use this + // interface to specify the sandbox policy for new processes created by + // SpawnTarget() + virtual TargetPolicy* CreatePolicy() = 0; + + // Creates a new target (child process) in a suspended state. + // Parameters: + // exe_path: This is the full path to the target binary. This parameter + // can be null and in this case the exe path must be the first argument + // of the command_line. + // command_line: The arguments to be passed as command line to the new + // process. This can be null if the exe_path parameter is not null. + // policy: This is the pointer to the policy object for the sandbox to + // be created. + // target: returns the resulting target process information such as process + // handle and PID just as if CreateProcess() had been called. The caller is + // responsible for closing the handles returned in this structure. + // Returns: + // ALL_OK if successful. All other return values imply failure. + virtual ResultCode SpawnTarget(const wchar_t* exe_path, + const wchar_t* command_line, + TargetPolicy* policy, + PROCESS_INFORMATION* target) = 0; + + // This call blocks (waits) for all the targets to terminate. + // Returns: + // ALL_OK if successful. All other return values imply failure. + // If the return is ERROR_GENERIC, you can call ::GetLastError() to get + // more information. + virtual ResultCode WaitForAllTargets() = 0; + + // Adds an unsandboxed process as a peer for policy decisions (e.g. + // HANDLES_DUP_ANY policy). + // Returns: + // ALL_OK if successful. All other return values imply failure. + // If the return is ERROR_GENERIC, you can call ::GetLastError() to get + // more information. + virtual ResultCode AddTargetPeer(HANDLE peer_process) = 0; +}; + +// TargetServices models the current process from the perspective +// of a target process. To obtain a pointer to it use +// Sandbox::GetTargetServices(). Note that this call returns a non-null +// pointer only if this process is in fact a target. A process is a target +// only if the process was spawned by a call to BrokerServices::SpawnTarget(). +// +// This API allows the target to gain access to resources with a high +// privilege token and then when it is ready to perform dangerous activities +// (such as download content from the web) it can lower its token and +// enter into locked-down (sandbox) mode. +// The typical usage is as follows: +// +// TargetServices* target_services = Sandbox::GetTargetServices(); +// if (NULL != target_services) { +// // We are the target. +// target_services->Init(); +// // Do work that requires high privileges here. +// // .... +// // When ready to enter lock-down mode call LowerToken: +// target_services->LowerToken(); +// } +// +// For more information see the BrokerServices API documentation. +class TargetServices { + public: + // Initializes the target. Must call this function before any other. + // returns ALL_OK if successful. All other return values imply failure. + // If the return is ERROR_GENERIC, you can call ::GetLastError() to get + // more information. + virtual ResultCode Init() = 0; + + // Discards the impersonation token and uses the lower token, call before + // processing any untrusted data or running third-party code. If this call + // fails the current process could be terminated immediately. + virtual void LowerToken() = 0; + + // Returns the ProcessState object. Through that object it's possible to have + // information about the current state of the process, such as whether + // LowerToken has been called or not. + virtual ProcessState* GetState() = 0; + + // Requests the broker to duplicate the supplied handle into the target + // process. The target process must be an active sandbox child process + // and the source process must have a corresponding policy allowing + // handle duplication for this object type. + // Returns: + // ALL_OK if successful. All other return values imply failure. + // If the return is ERROR_GENERIC, you can call ::GetLastError() to get + // more information. + virtual ResultCode DuplicateHandle(HANDLE source_handle, + DWORD target_process_id, + HANDLE* target_handle, + DWORD desired_access, + DWORD options) = 0; +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_SANDBOX_H__ diff --git a/sandbox/win/src/sandbox.vcproj b/sandbox/win/src/sandbox.vcproj new file mode 100644 index 0000000..f206e01 --- /dev/null +++ b/sandbox/win/src/sandbox.vcproj @@ -0,0 +1,658 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/win/src/sandbox_factory.h b/sandbox/win/src/sandbox_factory.h new file mode 100644 index 0000000..b52bcb6 --- /dev/null +++ b/sandbox/win/src/sandbox_factory.h @@ -0,0 +1,50 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SANDBOX_FACTORY_H__ +#define SANDBOX_SRC_SANDBOX_FACTORY_H__ + +#include "sandbox/src/sandbox.h" + +// SandboxFactory is a set of static methods to get access to the broker +// or target services object. Only one of the two methods (GetBrokerServices, +// GetTargetServices) will return a non-null pointer and that should be used +// as the indication that the process is the broker or the target: +// +// BrokerServices* broker_services = SandboxFactory::GetBrokerServices(); +// if (NULL != broker_services) { +// //we are the broker, call broker api here +// broker_services->Init(); +// } else { +// TargetServices* target_services = SandboxFactory::GetTargetServices(); +// if (NULL != target_services) { +// //we are the target, call target api here +// target_services->Init(); +// } +// +// The methods in this class are expected to be called from a single thread +// +// The Sandbox library needs to be linked against the main executable, but +// sometimes the API calls are issued from a DLL that loads into the exe +// process. These factory methods then need to be called from the main +// exe and the interface pointers then can be safely passed to the DLL where +// the Sandbox API calls are made. +namespace sandbox { + +class SandboxFactory { + public: + // Returns the Broker API interface, returns NULL if this process is the + // target. + static BrokerServices* GetBrokerServices(); + + // Returns the Target API interface, returns NULL if this process is the + // broker. + static TargetServices* GetTargetServices(); + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SandboxFactory); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SANDBOX_FACTORY_H__ diff --git a/sandbox/win/src/sandbox_nt_types.h b/sandbox/win/src/sandbox_nt_types.h new file mode 100644 index 0000000..d0d24f8 --- /dev/null +++ b/sandbox/win/src/sandbox_nt_types.h @@ -0,0 +1,46 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SANDBOX_NT_TYPES_H__ +#define SANDBOX_SRC_SANDBOX_NT_TYPES_H__ + +#include "sandbox/src/nt_internals.h" + +namespace sandbox { + +struct NtExports { + NtAllocateVirtualMemoryFunction AllocateVirtualMemory; + NtCloseFunction Close; + NtDuplicateObjectFunction DuplicateObject; + NtFreeVirtualMemoryFunction FreeVirtualMemory; + NtMapViewOfSectionFunction MapViewOfSection; + NtProtectVirtualMemoryFunction ProtectVirtualMemory; + NtQueryInformationProcessFunction QueryInformationProcess; + NtQueryObjectFunction QueryObject; + NtQuerySectionFunction QuerySection; + NtQueryVirtualMemoryFunction QueryVirtualMemory; + NtUnmapViewOfSectionFunction UnmapViewOfSection; + RtlAllocateHeapFunction RtlAllocateHeap; + RtlAnsiStringToUnicodeStringFunction RtlAnsiStringToUnicodeString; + RtlCompareUnicodeStringFunction RtlCompareUnicodeString; + RtlCreateHeapFunction RtlCreateHeap; + RtlCreateUserThreadFunction RtlCreateUserThread; + RtlDestroyHeapFunction RtlDestroyHeap; + RtlFreeHeapFunction RtlFreeHeap; + _strnicmpFunction _strnicmp; + strlenFunction strlen; + wcslenFunction wcslen; +}; + +// This is the value used for the ntdll level allocator. +enum AllocationType { + NT_ALLOC, + NT_PLACE, + NT_PAGE +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_SANDBOX_NT_TYPES_H__ diff --git a/sandbox/win/src/sandbox_nt_util.cc b/sandbox/win/src/sandbox_nt_util.cc new file mode 100644 index 0000000..daa361a --- /dev/null +++ b/sandbox/win/src/sandbox_nt_util.cc @@ -0,0 +1,599 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/sandbox_nt_util.h" + +#include "base/win/pe_image.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +// This is the list of all imported symbols from ntdll.dll. +SANDBOX_INTERCEPT NtExports g_nt = { NULL }; + +} + +namespace { + +#if defined(_WIN64) +void* AllocateNearTo(void* source, size_t size) { + using sandbox::g_nt; + + // Start with 1 GB above the source. + const unsigned int kOneGB = 0x40000000; + void* base = reinterpret_cast(source) + kOneGB; + SIZE_T actual_size = size; + ULONG_PTR zero_bits = 0; // Not the correct type if used. + ULONG type = MEM_RESERVE; + + if (reinterpret_cast(source) > 0x7ff80000000) { + // We are at the top of the address space. Let's try the highest available + // address. + base = NULL; + type |= MEM_TOP_DOWN; + } + + NTSTATUS ret; + int attempts = 0; + for (; attempts < 20; attempts++) { + ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits, + &actual_size, type, PAGE_READWRITE); + if (NT_SUCCESS(ret)) { + if (base < source) { + // We won't be able to patch this dll. + VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, + MEM_RELEASE)); + return NULL; + } + break; + } + + // Try 100 MB higher. + base = reinterpret_cast(base) + 100 * 0x100000; + }; + + if (attempts == 20) + return NULL; + + ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits, + &actual_size, MEM_COMMIT, PAGE_READWRITE); + + if (!NT_SUCCESS(ret)) { + VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, + MEM_RELEASE)); + base = NULL; + } + + return base; +} +#else // defined(_WIN64). +void* AllocateNearTo(void* source, size_t size) { + using sandbox::g_nt; + UNREFERENCED_PARAMETER(source); + + // In 32-bit processes allocations below 512k are predictable, so mark + // anything in that range as reserved and retry until we get a good address. + const void* const kMinAddress = reinterpret_cast(512 * 1024); + NTSTATUS ret; + SIZE_T actual_size; + void* base; + do { + base = NULL; + actual_size = 64 * 1024; + ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size, + MEM_RESERVE, PAGE_NOACCESS); + if (!NT_SUCCESS(ret)) + return NULL; + } while (base < kMinAddress); + + actual_size = size; + ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size, + MEM_COMMIT, PAGE_READWRITE); + if (!NT_SUCCESS(ret)) + return NULL; + return base; +} +#endif // defined(_WIN64). + +} // namespace. + +namespace sandbox { + +// Handle for our private heap. +void* g_heap = NULL; + +SANDBOX_INTERCEPT HANDLE g_shared_section; +SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0; +SANDBOX_INTERCEPT size_t g_shared_policy_size = 0; + +void* volatile g_shared_policy_memory = NULL; +void* volatile g_shared_IPC_memory = NULL; + +// Both the IPC and the policy share a single region of memory in which the IPC +// memory is first and the policy memory is last. +bool MapGlobalMemory() { + if (NULL == g_shared_IPC_memory) { + void* memory = NULL; + SIZE_T size = 0; + // Map the entire shared section from the start. + NTSTATUS ret = g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess, + &memory, 0, 0, NULL, &size, ViewUnmap, + 0, PAGE_READWRITE); + + if (!NT_SUCCESS(ret) || NULL == memory) { + NOTREACHED_NT(); + return false; + } + + if (NULL != _InterlockedCompareExchangePointer(&g_shared_IPC_memory, + memory, NULL)) { + // Somebody beat us to the memory setup. + ret = g_nt.UnmapViewOfSection(NtCurrentProcess, memory); + VERIFY_SUCCESS(ret); + } + DCHECK_NT(g_shared_IPC_size > 0); + g_shared_policy_memory = reinterpret_cast(g_shared_IPC_memory) + + g_shared_IPC_size; + } + DCHECK_NT(g_shared_policy_memory); + DCHECK_NT(g_shared_policy_size > 0); + return true; +} + +void* GetGlobalIPCMemory() { + if (!MapGlobalMemory()) + return NULL; + return g_shared_IPC_memory; +} + +void* GetGlobalPolicyMemory() { + if (!MapGlobalMemory()) + return NULL; + return g_shared_policy_memory; +} + +bool InitHeap() { + if (!g_heap) { + // Create a new heap using default values for everything. + void* heap = g_nt.RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); + if (!heap) + return false; + + if (NULL != _InterlockedCompareExchangePointer(&g_heap, heap, NULL)) { + // Somebody beat us to the memory setup. + g_nt.RtlDestroyHeap(heap); + } + } + return (g_heap) ? true : false; +} + +// Physically reads or writes from memory to verify that (at this time), it is +// valid. Returns a dummy value. +int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) { + const int kPageSize = 4096; + int dummy = 0; + char* start = reinterpret_cast(buffer); + char* end = start + size_bytes - 1; + + if (WRITE == intent) { + for (; start < end; start += kPageSize) { + *start = 0; + } + *end = 0; + } else { + for (; start < end; start += kPageSize) { + dummy += *start; + } + dummy += *end; + } + + return dummy; +} + +bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) { + DCHECK_NT(size); + __try { + TouchMemory(buffer, size, intent); + } __except(EXCEPTION_EXECUTE_HANDLER) { + return false; + } + return true; +} + +NTSTATUS CopyData(void* destination, const void* source, size_t bytes) { + NTSTATUS ret = STATUS_SUCCESS; + __try { + if (SandboxFactory::GetTargetServices()->GetState()->InitCalled()) { + memcpy(destination, source, bytes); + } else { + const char* from = reinterpret_cast(source); + char* to = reinterpret_cast(destination); + for (size_t i = 0; i < bytes; i++) { + to[i] = from[i]; + } + } + } __except(EXCEPTION_EXECUTE_HANDLER) { + ret = GetExceptionCode(); + } + return ret; +} + +// Hacky code... replace with AllocAndCopyObjectAttributes. +NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object, + wchar_t** out_name, uint32* attributes, + HANDLE* root) { + if (!InitHeap()) + return STATUS_NO_MEMORY; + + DCHECK_NT(out_name); + *out_name = NULL; + NTSTATUS ret = STATUS_UNSUCCESSFUL; + __try { + do { + if (in_object->RootDirectory != static_cast(0) && !root) + break; + if (NULL == in_object->ObjectName) + break; + if (NULL == in_object->ObjectName->Buffer) + break; + + size_t size = in_object->ObjectName->Length + sizeof(wchar_t); + *out_name = new(NT_ALLOC) wchar_t[size/sizeof(wchar_t)]; + if (NULL == *out_name) + break; + + ret = CopyData(*out_name, in_object->ObjectName->Buffer, + size - sizeof(wchar_t)); + if (!NT_SUCCESS(ret)) + break; + + (*out_name)[size / sizeof(wchar_t) - 1] = L'\0'; + + if (attributes) + *attributes = in_object->Attributes; + + if (root) + *root = in_object->RootDirectory; + ret = STATUS_SUCCESS; + } while (false); + } __except(EXCEPTION_EXECUTE_HANDLER) { + ret = GetExceptionCode(); + } + + if (!NT_SUCCESS(ret) && *out_name) { + operator delete(*out_name, NT_ALLOC); + *out_name = NULL; + } + + return ret; +} + +NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) { + PROCESS_BASIC_INFORMATION proc_info; + ULONG bytes_returned; + + NTSTATUS ret = g_nt.QueryInformationProcess(process, ProcessBasicInformation, + &proc_info, sizeof(proc_info), + &bytes_returned); + if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned) + return ret; + + *process_id = proc_info.UniqueProcessId; + return STATUS_SUCCESS; +} + +bool IsSameProcess(HANDLE process) { + if (NtCurrentProcess == process) + return true; + + static ULONG s_process_id = 0; + + if (!s_process_id) { + NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id); + if (!NT_SUCCESS(ret)) + return false; + } + + ULONG process_id; + NTSTATUS ret = GetProcessId(process, &process_id); + if (!NT_SUCCESS(ret)) + return false; + + return (process_id == s_process_id); +} + +bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset, + PSIZE_T view_size) { + if (!section || !base || !view_size || offset) + return false; + + HANDLE query_section; + + NTSTATUS ret = g_nt.DuplicateObject(NtCurrentProcess, section, + NtCurrentProcess, &query_section, + SECTION_QUERY, 0, 0); + if (!NT_SUCCESS(ret)) + return false; + + SECTION_BASIC_INFORMATION basic_info; + SIZE_T bytes_returned; + ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info, + sizeof(basic_info), &bytes_returned); + + VERIFY_SUCCESS(g_nt.Close(query_section)); + + if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned) + return false; + + if (!(basic_info.Attributes & SEC_IMAGE)) + return false; + + return true; +} + +UNICODE_STRING* AnsiToUnicode(const char* string) { + ANSI_STRING ansi_string; + ansi_string.Length = static_cast(g_nt.strlen(string)); + ansi_string.MaximumLength = ansi_string.Length + 1; + ansi_string.Buffer = const_cast(string); + + if (ansi_string.Length > ansi_string.MaximumLength) + return NULL; + + size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t) + + sizeof(UNICODE_STRING); + + UNICODE_STRING* out_string = reinterpret_cast( + new(NT_ALLOC) char[name_bytes]); + if (!out_string) + return NULL; + + out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t); + out_string->Buffer = reinterpret_cast(&out_string[1]); + + BOOLEAN alloc_destination = FALSE; + NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string, + alloc_destination); + DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret); + if (!NT_SUCCESS(ret)) { + operator delete(out_string, NT_ALLOC); + return NULL; + } + + return out_string; +} + +UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) { + UNICODE_STRING* out_name = NULL; + __try { + do { + *flags = 0; + base::win::PEImage pe(module); + + if (!pe.VerifyMagic()) + break; + *flags |= MODULE_IS_PE_IMAGE; + + PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory(); + if (exports) { + char* name = reinterpret_cast(pe.RVAToAddr(exports->Name)); + out_name = AnsiToUnicode(name); + } + + PIMAGE_NT_HEADERS headers = pe.GetNTHeaders(); + if (headers) { + if (headers->OptionalHeader.AddressOfEntryPoint) + *flags |= MODULE_HAS_ENTRY_POINT; + if (headers->OptionalHeader.SizeOfCode) + *flags |= MODULE_HAS_CODE; + } + } while (false); + } __except(EXCEPTION_EXECUTE_HANDLER) { + } + + return out_name; +} + +UNICODE_STRING* GetBackingFilePath(PVOID address) { + // We'll start with something close to max_path charactes for the name. + ULONG buffer_bytes = MAX_PATH * 2; + + for (;;) { + MEMORY_SECTION_NAME* section_name = reinterpret_cast( + new(NT_ALLOC) char[buffer_bytes]); + + if (!section_name) + return NULL; + + ULONG returned_bytes; + NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address, + MemorySectionName, section_name, + buffer_bytes, &returned_bytes); + + if (STATUS_BUFFER_OVERFLOW == ret) { + // Retry the call with the given buffer size. + operator delete(section_name, NT_ALLOC); + section_name = NULL; + buffer_bytes = returned_bytes; + continue; + } + if (!NT_SUCCESS(ret)) { + operator delete(section_name, NT_ALLOC); + return NULL; + } + + return reinterpret_cast(section_name); + } +} + +UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) { + if ((!module_path) || (!module_path->Buffer)) + return NULL; + + wchar_t* sep = NULL; + int start_pos = module_path->Length / sizeof(wchar_t) - 1; + int ix = start_pos; + + for (; ix >= 0; --ix) { + if (module_path->Buffer[ix] == L'\\') { + sep = &module_path->Buffer[ix]; + break; + } + } + + // Ends with path separator. Not a valid module name. + if ((ix == start_pos) && sep) + return NULL; + + // No path separator found. Use the entire name. + if (!sep) { + sep = &module_path->Buffer[-1]; + } + + // Add one to the size so we can null terminate the string. + size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t); + + // Based on the code above, size_bytes should always be small enough + // to make the static_cast below safe. + DCHECK_NT(kuint16max > size_bytes); + char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)]; + if (!str_buffer) + return NULL; + + UNICODE_STRING* out_string = reinterpret_cast(str_buffer); + out_string->Buffer = reinterpret_cast(&out_string[1]); + out_string->Length = static_cast(size_bytes - sizeof(wchar_t)); + out_string->MaximumLength = static_cast(size_bytes); + + NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length); + if (!NT_SUCCESS(ret)) { + operator delete(out_string, NT_ALLOC); + return NULL; + } + + out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0'; + return out_string; +} + +NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes, + ULONG protect) { + DCHECK_NT(!changed_); + SIZE_T new_bytes = bytes; + NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address, + &new_bytes, protect, &old_protect_); + if (NT_SUCCESS(ret)) { + changed_ = true; + address_ = address; + bytes_ = new_bytes; + } + + return ret; +} + +NTSTATUS AutoProtectMemory::RevertProtection() { + if (!changed_) + return STATUS_SUCCESS; + + DCHECK_NT(address_); + DCHECK_NT(bytes_); + + SIZE_T new_bytes = bytes_; + NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address_, + &new_bytes, old_protect_, + &old_protect_); + DCHECK_NT(NT_SUCCESS(ret)); + + changed_ = false; + address_ = NULL; + bytes_ = 0; + old_protect_ = 0; + + return ret; +} + +bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, + uint32 file_info_class) { + if (FileRenameInformation != file_info_class) + return false; + + if (length < sizeof(FILE_RENAME_INFORMATION)) + return false; + + // Make sure file name length doesn't exceed the message length + if (length - offsetof(FILE_RENAME_INFORMATION, FileName) < + file_info->FileNameLength) + return false; + + // We don't support a root directory. + if (file_info->RootDirectory) + return false; + + // Check if it starts with \\??\\. We don't support relative paths. + if (file_info->FileNameLength < 4 || file_info->FileNameLength > kuint16max) + return false; + + if (file_info->FileName[0] != L'\\' || + file_info->FileName[1] != L'?' || + file_info->FileName[2] != L'?' || + file_info->FileName[3] != L'\\') + return false; + + return true; +} + +} // namespace sandbox + +void* operator new(size_t size, sandbox::AllocationType type, + void* near_to) { + using namespace sandbox; + + if (NT_ALLOC == type) { + if (!InitHeap()) + return NULL; + + // Use default flags for the allocation. + return g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size); + } else if (NT_PAGE == type) { + return AllocateNearTo(near_to, size); + } + NOTREACHED_NT(); + return NULL; +} + +void operator delete(void* memory, sandbox::AllocationType type) { + using namespace sandbox; + + if (NT_ALLOC == type) { + // Use default flags. + VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory)); + } else if (NT_PAGE == type) { + void* base = memory; + SIZE_T size = 0; + VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, + MEM_RELEASE)); + } else { + NOTREACHED_NT(); + } +} + +void operator delete(void* memory, sandbox::AllocationType type, + void* near_to) { + UNREFERENCED_PARAMETER(near_to); + operator delete(memory, type); +} + +void* __cdecl operator new(size_t size, void* buffer, + sandbox::AllocationType type) { + UNREFERENCED_PARAMETER(size); + UNREFERENCED_PARAMETER(type); + return buffer; +} + +void __cdecl operator delete(void* memory, void* buffer, + sandbox::AllocationType type) { + UNREFERENCED_PARAMETER(memory); + UNREFERENCED_PARAMETER(buffer); + UNREFERENCED_PARAMETER(type); +} diff --git a/sandbox/win/src/sandbox_nt_util.h b/sandbox/win/src/sandbox_nt_util.h new file mode 100644 index 0000000..7a82302 --- /dev/null +++ b/sandbox/win/src/sandbox_nt_util.h @@ -0,0 +1,173 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SANDBOX_NT_UTIL_H_ +#define SANDBOX_SRC_SANDBOX_NT_UTIL_H_ + +#include "base/basictypes.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_nt_types.h" + +// Placement new and delete to be used from ntdll interception code. +void* __cdecl operator new(size_t size, sandbox::AllocationType type, + void* near_to = NULL); +void __cdecl operator delete(void* memory, sandbox::AllocationType type); +// Add operator delete that matches the placement form of the operator new +// above. This is required by compiler to generate code to call operator delete +// in case the object's constructor throws an exception. +// See http://msdn.microsoft.com/en-us/library/cxdxz3x6.aspx +void __cdecl operator delete(void* memory, sandbox::AllocationType type, + void* near_to); + +// Regular placement new and delete +void* __cdecl operator new(size_t size, void* buffer, + sandbox::AllocationType type); +void __cdecl operator delete(void* memory, void* buffer, + sandbox::AllocationType type); + +// DCHECK_NT is defined to be pretty much an assert at this time because we +// don't have logging from the ntdll layer on the child. +// +// VERIFY_NT and VERIFY_SUCCESS_NT are the standard asserts on debug, but +// execute the actual argument on release builds. VERIFY_NT expects an action +// returning a bool, while VERIFY_SUCCESS_NT expects an action returning +// NTSTATUS. +#ifndef NDEBUG +#define DCHECK_NT(condition) { (condition) ? 0 : __debugbreak(); } +#define VERIFY(action) DCHECK_NT(action) +#define VERIFY_SUCCESS(action) DCHECK_NT(NT_SUCCESS(action)) +#else +#define DCHECK_NT(condition) +#define VERIFY(action) (action) +#define VERIFY_SUCCESS(action) (action) +#endif + +#define NOTREACHED_NT() DCHECK_NT(false) + +namespace sandbox { + +extern "C" long _InterlockedCompareExchange(long volatile* destination, + long exchange, long comperand); + +#pragma intrinsic(_InterlockedCompareExchange) + +// We want to make sure that we use an intrinsic version of the function, not +// the one provided by kernel32. +__forceinline void* _InterlockedCompareExchangePointer( + void* volatile* destination, void* exchange, void* comperand) { + size_t ret = _InterlockedCompareExchange( + reinterpret_cast(destination), + static_cast(reinterpret_cast(exchange)), + static_cast(reinterpret_cast(comperand))); + + return reinterpret_cast(static_cast(ret)); +} + +// Returns a pointer to the IPC shared memory. +void* GetGlobalIPCMemory(); + +// Returns a pointer to the Policy shared memory. +void* GetGlobalPolicyMemory(); + +enum RequiredAccess { + READ, + WRITE +}; + +// Performs basic user mode buffer validation. In any case, buffers access must +// be protected by SEH. intent specifies if the buffer should be tested for read +// or write. +// Note that write intent implies destruction of the buffer content (we actually +// write) +bool ValidParameter(void* buffer, size_t size, RequiredAccess intent); + + +// Copies data from a user buffer to our buffer. Returns the operation status. +NTSTATUS CopyData(void* destination, const void* source, size_t bytes); + +// Copies the name from an object attributes. +NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object, + wchar_t** out_name, uint32* attributes, HANDLE* root); + +// Initializes our ntdll level heap +bool InitHeap(); + +// Returns true if the provided handle refers to the current process. +bool IsSameProcess(HANDLE process); + +enum MappedModuleFlags { + MODULE_IS_PE_IMAGE = 1, // Module is an executable. + MODULE_HAS_ENTRY_POINT = 2, // Execution entry point found. + MODULE_HAS_CODE = 4 // Non zero size of executable sections. +}; + +// Returns the name and characteristics for a given PE module. The return +// value is the name as defined by the export table and the flags is any +// combination of the MappedModuleFlags enumeration. +// +// The returned buffer must be freed with a placement delete from the ntdll +// level allocator: +// +// UNICODE_STRING* name = GetPEImageInfoFromModule(HMODULE module, &flags); +// if (!name) { +// // probably not a valid dll +// return; +// } +// InsertYourLogicHere(name); +// operator delete(name, NT_ALLOC); +UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags); + +// Returns the full path and filename for a given dll. +// May return NULL if the provided address is not backed by a named section, or +// if the current OS version doesn't support the call. The returned buffer must +// be freed with a placement delete (see GetImageNameFromModule example). +UNICODE_STRING* GetBackingFilePath(PVOID address); + +// Returns the last component of a path that contains the module name. +// It will return NULL if the path ends with the path separator. The returned +// buffer must be freed with a placement delete (see GetImageNameFromModule +// example). +UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path); + +// Returns true if the parameters correspond to a dll mapped as code. +bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset, + PSIZE_T view_size); + +// Converts an ansi string to an UNICODE_STRING. +UNICODE_STRING* AnsiToUnicode(const char* string); + +// Provides a simple way to temporarily change the protection of a memory page. +class AutoProtectMemory { + public: + AutoProtectMemory() + : changed_(false), address_(NULL), bytes_(0), old_protect_(0) {} + + ~AutoProtectMemory() { + RevertProtection(); + } + + // Sets the desired protection of a given memory range. + NTSTATUS ChangeProtection(void* address, size_t bytes, ULONG protect); + + // Restores the original page protection. + NTSTATUS RevertProtection(); + + private: + bool changed_; + void* address_; + size_t bytes_; + ULONG old_protect_; + + DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory); +}; + +// Returns true if the file_rename_information structure is supported by our +// rename handler. +bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, + uint32 file_info_class); + +} // namespace sandbox + + +#endif // SANDBOX_SRC_SANDBOX_NT_UTIL_H__ diff --git a/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h new file mode 100644 index 0000000..288adc45 --- /dev/null +++ b/sandbox/win/src/sandbox_policy.h @@ -0,0 +1,190 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SANDBOX_POLICY_H_ +#define SANDBOX_SRC_SANDBOX_POLICY_H_ + +#include + +#include "base/basictypes.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/security_level.h" + +namespace sandbox { + +class TargetPolicy { + public: + // Windows subsystems that can have specific rules. + // Note: The process subsystem(SUBSY_PROCESS) does not evaluate the request + // exactly like the CreateProcess API does. See the comment at the top of + // process_thread_dispatcher.cc for more details. + enum SubSystem { + SUBSYS_FILES, // Creation and opening of files and pipes. + SUBSYS_NAMED_PIPES, // Creation of named pipes. + SUBSYS_PROCESS, // Creation of child processes. + SUBSYS_REGISTRY, // Creation and opening of registry keys. + SUBSYS_SYNC, // Creation of named sync objects. + SUBSYS_HANDLES // Duplication of handles to other processes. + }; + + // Allowable semantics when a rule is matched. + enum Semantics { + FILES_ALLOW_ANY, // Allows open or create for any kind of access that + // the file system supports. + FILES_ALLOW_READONLY, // Allows open or create with read access only. + FILES_ALLOW_QUERY, // Allows access to query the attributes of a file. + FILES_ALLOW_DIR_ANY, // Allows open or create with directory semantics + // only. + HANDLES_DUP_ANY, // Allows duplicating handles opened with any + // access permissions. + HANDLES_DUP_BROKER, // Allows duplicating handles to the broker process. + NAMEDPIPES_ALLOW_ANY, // Allows creation of a named pipe. + PROCESS_MIN_EXEC, // Allows to create a process with minimal rights + // over the resulting process and thread handles. + // No other parameters besides the command line are + // passed to the child process. + PROCESS_ALL_EXEC, // Allows the creation of a process and return fill + // access on the returned handles. + // This flag can be used only when the main token of + // the sandboxed application is at least INTERACTIVE. + EVENTS_ALLOW_ANY, // Allows the creation of an event with full access. + EVENTS_ALLOW_READONLY, // Allows opening an even with synchronize access. + REG_ALLOW_READONLY, // Allows readonly access to a registry key. + REG_ALLOW_ANY // Allows read and write access to a registry key. + }; + + // Increments the reference count of this object. The reference count must + // be incremented if this interface is given to another component. + virtual void AddRef() = 0; + + // Decrements the reference count of this object. When the reference count + // is zero the object is automatically destroyed. + // Indicates that the caller is done with this interface. After calling + // release no other method should be called. + virtual void Release() = 0; + + // Sets the security level for the target process' two tokens. + // This setting is permanent and cannot be changed once the target process is + // spawned. + // initial: the security level for the initial token. This is the token that + // is used by the process from the creation of the process until the moment + // the process calls TargetServices::LowerToken() or the process calls + // win32's ReverToSelf(). Once this happens the initial token is no longer + // available and the lockdown token is in effect. + // lockdown: the security level for the token that comes into force after the + // process calls TargetServices::LowerToken() or the process calls + // ReverToSelf(). See the explanation of each level in the TokenLevel + // definition. + // Return value: SBOX_ALL_OK if the setting succeeds and false otherwise. + // Returns false if the lockdown value is more permissive than the initial + // value. + // + // Important: most of the sandbox-provided security relies on this single + // setting. The caller should strive to set the lockdown level as restricted + // as possible. + virtual ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) = 0; + + // Sets the security level of the Job Object to which the target process will + // belong. This setting is permanent and cannot be changed once the target + // process is spawned. The job controls the global security settings which + // can not be specified in the token security profile. + // job_level: the security level for the job. See the explanation of each + // level in the JobLevel definition. + // ui_exceptions: specify what specific rights that are disabled in the + // chosen job_level that need to be granted. Use this parameter to avoid + // selecting the next permissive job level unless you need all the rights + // that are granted in such level. + // The exceptions can be specified as a combination of the following + // constants: + // JOB_OBJECT_UILIMIT_HANDLES : grant access to all user-mode handles. These + // include windows, icons, menus and various GDI objects. In addition the + // target process can set hooks, and broadcast messages to other processes + // that belong to the same desktop. + // JOB_OBJECT_UILIMIT_READCLIPBOARD : grant read-only access to the clipboard. + // JOB_OBJECT_UILIMIT_WRITECLIPBOARD : grant write access to the clipboard. + // JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS : allow changes to the system-wide + // parameters as defined by the Win32 call SystemParametersInfo(). + // JOB_OBJECT_UILIMIT_DISPLAYSETTINGS : allow programmatic changes to the + // display settings. + // JOB_OBJECT_UILIMIT_GLOBALATOMS : allow access to the global atoms table. + // JOB_OBJECT_UILIMIT_DESKTOP : allow the creation of new desktops. + // JOB_OBJECT_UILIMIT_EXITWINDOWS : allow the call to ExitWindows(). + // + // Return value: SBOX_ALL_OK if the setting succeeds and false otherwise. + // + // Note: JOB_OBJECT_XXXX constants are defined in winnt.h and documented at + // length in: + // http://msdn2.microsoft.com/en-us/library/ms684152.aspx + // + // Note: the recommended level is JOB_RESTRICTED or JOB_LOCKDOWN. + virtual ResultCode SetJobLevel(JobLevel job_level, uint32 ui_exceptions) = 0; + + // Specifies the desktop on which the application is going to run. If the + // desktop does not exist, it will be created. If alternate_winstation is + // set to true, the desktop will be created on an alternate window station. + virtual ResultCode SetAlternateDesktop(bool alternate_winstation) = 0; + + // Returns the name of the alternate desktop used. If an alternate window + // station is specified, the name is prepended by the window station name, + // followed by a backslash. + virtual std::wstring GetAlternateDesktop() const = 0; + + // Precreates the desktop and window station, if any. + virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) = 0; + + // Destroys the desktop and windows station. + virtual void DestroyAlternateDesktop() = 0; + + // Sets the integrity level of the process in the sandbox. Both the initial + // token and the main token will be affected by this. This is valid only + // on Vista. It is silently ignored on other OSes. If you set the integrity + // level to a level higher than your current level, the sandbox will fail + // to start. + virtual ResultCode SetIntegrityLevel(IntegrityLevel level) = 0; + + // Sets the integrity level of the process in the sandbox. The integrity level + // will not take effect before you call LowerToken. User Interface Privilege + // Isolation is not affected by this setting and will remain off for the + // process in the sandbox. This flag is valid on Vista only, it is silently + // ignored on other OSes. If you set the integrity level to a level higher + // than your current level, the sandbox will fail to start. + virtual ResultCode SetDelayedIntegrityLevel(IntegrityLevel level) = 0; + + // Sets the interceptions to operate in strict mode. By default, interceptions + // are performed in "relaxed" mode, where if something inside NTDLL.DLL is + // already patched we attempt to intercept it anyway. Setting interceptions + // to strict mode means that when we detect that the function is patched we'll + // refuse to perform the interception. + virtual void SetStrictInterceptions() = 0; + + // Adds a policy rule effective for processes spawned using this policy. + // subsystem: One of the above enumerated windows subsystems. + // semantics: One of the above enumerated FileSemantics. + // pattern: A specific full path or a full path with wildcard patterns. + // The valid wildcards are: + // '*' : Matches zero or more character. Only one in series allowed. + // '?' : Matches a single character. One or more in series are allowed. + // Examples: + // "c:\\documents and settings\\vince\\*.dmp" + // "c:\\documents and settings\\*\\crashdumps\\*.dmp" + // "c:\\temp\\app_log_?????_chrome.txt" + virtual ResultCode AddRule(SubSystem subsystem, Semantics semantics, + const wchar_t* pattern) = 0; + + // Adds a dll that will be unloaded in the target process before it gets + // a chance to initialize itself. Typically, dlls that cause the target + // to crash go here. + virtual ResultCode AddDllToUnload(const wchar_t* dll_name) = 0; + + // Adds a handle that will be closed in the target process after lockdown. + // A NULL value for handle_name indicates all handles of the specified type. + // An empty string for handle_name indicates the handle is unnamed. + virtual ResultCode AddKernelObjectToClose(const wchar_t* handle_type, + const wchar_t* handle_name) = 0; +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_SANDBOX_POLICY_H_ diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc new file mode 100644 index 0000000..4438641 --- /dev/null +++ b/sandbox/win/src/sandbox_policy_base.cc @@ -0,0 +1,542 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/sandbox_policy_base.h" + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/logging.h" +#include "sandbox/src/filesystem_dispatcher.h" +#include "sandbox/src/filesystem_policy.h" +#include "sandbox/src/handle_dispatcher.h" +#include "sandbox/src/handle_policy.h" +#include "sandbox/src/job.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/named_pipe_dispatcher.h" +#include "sandbox/src/named_pipe_policy.h" +#include "sandbox/src/policy_broker.h" +#include "sandbox/src/policy_engine_processor.h" +#include "sandbox/src/policy_low_level.h" +#include "sandbox/src/process_thread_dispatcher.h" +#include "sandbox/src/process_thread_policy.h" +#include "sandbox/src/registry_dispatcher.h" +#include "sandbox/src/registry_policy.h" +#include "sandbox/src/restricted_token_utils.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/sync_dispatcher.h" +#include "sandbox/src/sync_policy.h" +#include "sandbox/src/target_process.h" +#include "sandbox/src/window.h" + +namespace { +// The standard windows size for one memory page. +const size_t kOneMemPage = 4096; +// The IPC and Policy shared memory sizes. +const size_t kIPCMemSize = kOneMemPage * 2; +const size_t kPolMemSize = kOneMemPage * 14; + +// Helper function to allocate space (on the heap) for policy. +sandbox::PolicyGlobal* MakeBrokerPolicyMemory() { + const size_t kTotalPolicySz = kPolMemSize; + char* mem = new char[kTotalPolicySz]; + DCHECK(mem); + memset(mem, 0, kTotalPolicySz); + sandbox::PolicyGlobal* policy = reinterpret_cast(mem); + policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal); + return policy; +} +} + +namespace sandbox { + +SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level; + +// Initializes static members. +HWINSTA PolicyBase::alternate_winstation_handle_ = NULL; +HDESK PolicyBase::alternate_desktop_handle_ = NULL; + +PolicyBase::PolicyBase() + : ref_count(1), + lockdown_level_(USER_LOCKDOWN), + initial_level_(USER_LOCKDOWN), + job_level_(JOB_LOCKDOWN), + ui_exceptions_(0), + use_alternate_desktop_(false), + use_alternate_winstation_(false), + file_system_init_(false), + relaxed_interceptions_(true), + integrity_level_(INTEGRITY_LEVEL_LAST), + delayed_integrity_level_(INTEGRITY_LEVEL_LAST), + policy_maker_(NULL), + policy_(NULL) { + ::InitializeCriticalSection(&lock_); + // Initialize the IPC dispatcher array. + memset(&ipc_targets_, NULL, sizeof(ipc_targets_)); + Dispatcher* dispatcher = NULL; + + dispatcher = new FilesystemDispatcher(this); + ipc_targets_[IPC_NTCREATEFILE_TAG] = dispatcher; + ipc_targets_[IPC_NTOPENFILE_TAG] = dispatcher; + ipc_targets_[IPC_NTSETINFO_RENAME_TAG] = dispatcher; + ipc_targets_[IPC_NTQUERYATTRIBUTESFILE_TAG] = dispatcher; + ipc_targets_[IPC_NTQUERYFULLATTRIBUTESFILE_TAG] = dispatcher; + + dispatcher = new NamedPipeDispatcher(this); + ipc_targets_[IPC_CREATENAMEDPIPEW_TAG] = dispatcher; + + dispatcher = new ThreadProcessDispatcher(this); + ipc_targets_[IPC_NTOPENTHREAD_TAG] = dispatcher; + ipc_targets_[IPC_NTOPENPROCESS_TAG] = dispatcher; + ipc_targets_[IPC_CREATEPROCESSW_TAG] = dispatcher; + ipc_targets_[IPC_NTOPENPROCESSTOKEN_TAG] = dispatcher; + ipc_targets_[IPC_NTOPENPROCESSTOKENEX_TAG] = dispatcher; + + dispatcher = new SyncDispatcher(this); + ipc_targets_[IPC_CREATEEVENT_TAG] = dispatcher; + ipc_targets_[IPC_OPENEVENT_TAG] = dispatcher; + + dispatcher = new RegistryDispatcher(this); + ipc_targets_[IPC_NTCREATEKEY_TAG] = dispatcher; + ipc_targets_[IPC_NTOPENKEY_TAG] = dispatcher; + + dispatcher = new HandleDispatcher(this); + ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG] = dispatcher; +} + +PolicyBase::~PolicyBase() { + TargetSet::iterator it; + for (it = targets_.begin(); it != targets_.end(); ++it) { + TargetProcess* target = (*it); + delete target; + } + delete ipc_targets_[IPC_NTCREATEFILE_TAG]; + delete ipc_targets_[IPC_CREATENAMEDPIPEW_TAG]; + delete ipc_targets_[IPC_NTOPENTHREAD_TAG]; + delete ipc_targets_[IPC_CREATEEVENT_TAG]; + delete ipc_targets_[IPC_NTCREATEKEY_TAG]; + delete ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG]; + delete policy_maker_; + delete policy_; + ::DeleteCriticalSection(&lock_); +} + +void PolicyBase::AddRef() { + ::InterlockedIncrement(&ref_count); +} + +void PolicyBase::Release() { + if (0 == ::InterlockedDecrement(&ref_count)) + delete this; +} + +ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) { + if (initial < lockdown) { + return SBOX_ERROR_BAD_PARAMS; + } + initial_level_ = initial; + lockdown_level_ = lockdown; + return SBOX_ALL_OK; +} + +ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) { + job_level_ = job_level; + ui_exceptions_ = ui_exceptions; + return SBOX_ALL_OK; +} + +ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) { + use_alternate_desktop_ = true; + use_alternate_winstation_ = alternate_winstation; + return CreateAlternateDesktop(alternate_winstation); +} + +std::wstring PolicyBase::GetAlternateDesktop() const { + // No alternate desktop or winstation. Return an empty string. + if (!use_alternate_desktop_ && !use_alternate_winstation_) { + return std::wstring(); + } + + // The desktop and winstation should have been created by now. + // If we hit this scenario, it means that the user ignored the failure + // during SetAlternateDesktop, so we ignore it here too. + if (use_alternate_desktop_ && !alternate_desktop_handle_) { + return std::wstring(); + } + if (use_alternate_winstation_ && (!alternate_desktop_handle_ || + !alternate_winstation_handle_)) { + return std::wstring(); + } + + return GetFullDesktopName(alternate_winstation_handle_, + alternate_desktop_handle_); +} + +ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) { + if (alternate_winstation) { + // Previously called with alternate_winstation = false? + if (!alternate_winstation_handle_ && alternate_desktop_handle_) + return SBOX_ERROR_UNSUPPORTED; + + // Check if it's already created. + if (alternate_winstation_handle_ && alternate_desktop_handle_) + return SBOX_ALL_OK; + + DCHECK(!alternate_winstation_handle_); + // Create the window station. + ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_); + if (SBOX_ALL_OK != result) + return result; + + // Verify that everything is fine. + if (!alternate_winstation_handle_ || + GetWindowObjectName(alternate_winstation_handle_).empty()) + return SBOX_ERROR_CANNOT_CREATE_DESKTOP; + + // Create the destkop. + result = CreateAltDesktop(alternate_winstation_handle_, + &alternate_desktop_handle_); + if (SBOX_ALL_OK != result) + return result; + + // Verify that everything is fine. + if (!alternate_desktop_handle_ || + GetWindowObjectName(alternate_desktop_handle_).empty()) + return SBOX_ERROR_CANNOT_CREATE_DESKTOP; + } else { + // Previously called with alternate_winstation = true? + if (alternate_winstation_handle_) + return SBOX_ERROR_UNSUPPORTED; + + // Check if it already exists. + if (alternate_desktop_handle_) + return SBOX_ALL_OK; + + // Create the destkop. + ResultCode result = CreateAltDesktop(NULL, &alternate_desktop_handle_); + if (SBOX_ALL_OK != result) + return result; + + // Verify that everything is fine. + if (!alternate_desktop_handle_ || + GetWindowObjectName(alternate_desktop_handle_).empty()) + return SBOX_ERROR_CANNOT_CREATE_DESKTOP; + } + + return SBOX_ALL_OK; +} + +void PolicyBase::DestroyAlternateDesktop() { + if (alternate_desktop_handle_) { + ::CloseDesktop(alternate_desktop_handle_); + alternate_desktop_handle_ = NULL; + } + + if (alternate_winstation_handle_) { + ::CloseWindowStation(alternate_winstation_handle_); + alternate_winstation_handle_ = NULL; + } +} + +ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) { + integrity_level_ = integrity_level; + return SBOX_ALL_OK; +} + +ResultCode PolicyBase::SetDelayedIntegrityLevel( + IntegrityLevel integrity_level) { + delayed_integrity_level_ = integrity_level; + return SBOX_ALL_OK; +} + +void PolicyBase::SetStrictInterceptions() { + relaxed_interceptions_ = false; +} + +ResultCode PolicyBase::AddRule(SubSystem subsystem, Semantics semantics, + const wchar_t* pattern) { + if (NULL == policy_) { + policy_ = MakeBrokerPolicyMemory(); + DCHECK(policy_); + policy_maker_ = new LowLevelPolicy(policy_); + DCHECK(policy_maker_); + } + + switch (subsystem) { + case SUBSYS_FILES: { + if (!file_system_init_) { + if (!FileSystemPolicy::SetInitialRules(policy_maker_)) + return SBOX_ERROR_BAD_PARAMS; + file_system_init_ = true; + } + if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) { + NOTREACHED(); + return SBOX_ERROR_BAD_PARAMS; + } + break; + } + case SUBSYS_SYNC: { + if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) { + NOTREACHED(); + return SBOX_ERROR_BAD_PARAMS; + } + break; + } + case SUBSYS_PROCESS: { + if (lockdown_level_ < USER_INTERACTIVE && + TargetPolicy::PROCESS_ALL_EXEC == semantics) { + // This is unsupported. This is a huge security risk to give full access + // to a process handle. + return SBOX_ERROR_UNSUPPORTED; + } + if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) { + NOTREACHED(); + return SBOX_ERROR_BAD_PARAMS; + } + break; + } + case SUBSYS_NAMED_PIPES: { + if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) { + NOTREACHED(); + return SBOX_ERROR_BAD_PARAMS; + } + break; + } + case SUBSYS_REGISTRY: { + if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) { + NOTREACHED(); + return SBOX_ERROR_BAD_PARAMS; + } + break; + } + case SUBSYS_HANDLES: { + if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) { + NOTREACHED(); + return SBOX_ERROR_BAD_PARAMS; + } + break; + } + default: { + return SBOX_ERROR_UNSUPPORTED; + } + } + + return SBOX_ALL_OK; +} + +ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) { + blacklisted_dlls_.push_back(std::wstring(dll_name)); + return SBOX_ALL_OK; +} + +ResultCode PolicyBase::AddKernelObjectToClose(const char16* handle_type, + const char16* handle_name) { + return handle_closer_.AddHandle(handle_type, handle_name); +} + +// When an IPC is ready in any of the targets we get called. We manage an array +// of IPC dispatchers which are keyed on the IPC tag so we normally delegate +// to the appropriate dispatcher unless we can handle the IPC call ourselves. +Dispatcher* PolicyBase::OnMessageReady(IPCParams* ipc, + CallbackGeneric* callback) { + DCHECK(callback); + static const IPCParams ping1 = {IPC_PING1_TAG, ULONG_TYPE}; + static const IPCParams ping2 = {IPC_PING2_TAG, INOUTPTR_TYPE}; + + if (ping1.Matches(ipc) || ping2.Matches(ipc)) { + *callback = reinterpret_cast( + static_cast(&PolicyBase::Ping)); + return this; + } + + Dispatcher* dispatch = GetDispatcher(ipc->ipc_tag); + if (!dispatch) { + NOTREACHED(); + return NULL; + } + return dispatch->OnMessageReady(ipc, callback); +} + +// Delegate to the appropriate dispatcher. +bool PolicyBase::SetupService(InterceptionManager* manager, int service) { + if (IPC_PING1_TAG == service || IPC_PING2_TAG == service) + return true; + + Dispatcher* dispatch = GetDispatcher(service); + if (!dispatch) { + NOTREACHED(); + return false; + } + return dispatch->SetupService(manager, service); +} + +DWORD PolicyBase::MakeJobObject(HANDLE* job) { + // Create the windows job object. + Job job_obj; + DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_); + if (ERROR_SUCCESS != result) { + return result; + } + *job = job_obj.Detach(); + return ERROR_SUCCESS; +} + +DWORD PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) { + // Create the 'naked' token. This will be the permanent token associated + // with the process and therefore with any thread that is not impersonating. + DWORD result = CreateRestrictedToken(lockdown, lockdown_level_, + integrity_level_, PRIMARY); + if (ERROR_SUCCESS != result) { + return result; + } + // Create the 'better' token. We use this token as the one that the main + // thread uses when booting up the process. It should contain most of + // what we need (before reaching main( )) + result = CreateRestrictedToken(initial, initial_level_, + integrity_level_, IMPERSONATION); + if (ERROR_SUCCESS != result) { + ::CloseHandle(*lockdown); + return result; + } + return SBOX_ALL_OK; +} + +bool PolicyBase::AddTarget(TargetProcess* target) { + if (NULL != policy_) + policy_maker_->Done(); + + if (!SetupAllInterceptions(target)) + return false; + + if (!SetupHandleCloser(target)) + return false; + + // Initialize the sandbox infrastructure for the target. + if (ERROR_SUCCESS != target->Init(this, policy_, kIPCMemSize, kPolMemSize)) + return false; + + g_shared_delayed_integrity_level = delayed_integrity_level_; + ResultCode ret = target->TransferVariable( + "g_shared_delayed_integrity_level", + &g_shared_delayed_integrity_level, + sizeof(g_shared_delayed_integrity_level)); + g_shared_delayed_integrity_level = INTEGRITY_LEVEL_LAST; + if (SBOX_ALL_OK != ret) + return false; + + AutoLock lock(&lock_); + targets_.push_back(target); + return true; +} + +bool PolicyBase::OnJobEmpty(HANDLE job) { + AutoLock lock(&lock_); + TargetSet::iterator it; + for (it = targets_.begin(); it != targets_.end(); ++it) { + if ((*it)->Job() == job) + break; + } + if (it == targets_.end()) { + return false; + } + TargetProcess* target = *it; + targets_.erase(it); + delete target; + return true; +} + +EvalResult PolicyBase::EvalPolicy(int service, + CountedParameterSetBase* params) { + if (NULL != policy_) { + if (NULL == policy_->entry[service]) { + // There is no policy for this particular service. This is not a big + // deal. + return DENY_ACCESS; + } + for (int i = 0; i < params->count; i++) { + if (!params->parameters[i].IsValid()) { + NOTREACHED(); + return SIGNAL_ALARM; + } + } + PolicyProcessor pol_evaluator(policy_->entry[service]); + PolicyResult result = pol_evaluator.Evaluate(kShortEval, + params->parameters, + params->count); + if (POLICY_MATCH == result) { + return pol_evaluator.GetAction(); + } + DCHECK(POLICY_ERROR != result); + } + + return DENY_ACCESS; +} + +// We service IPC_PING_TAG message which is a way to test a round trip of the +// IPC subsystem. We receive a integer cookie and we are expected to return the +// cookie times two (or three) and the current tick count. +bool PolicyBase::Ping(IPCInfo* ipc, void* arg1) { + switch (ipc->ipc_tag) { + case IPC_PING1_TAG: { + IPCInt ipc_int(arg1); + uint32 cookie = ipc_int.As32Bit(); + ipc->return_info.extended_count = 2; + ipc->return_info.extended[0].unsigned_int = ::GetTickCount(); + ipc->return_info.extended[1].unsigned_int = 2 * cookie; + return true; + } + case IPC_PING2_TAG: { + CountedBuffer* io_buffer = reinterpret_cast(arg1); + if (sizeof(uint32) != io_buffer->Size()) + return false; + + uint32* cookie = reinterpret_cast(io_buffer->Buffer()); + *cookie = (*cookie) * 3; + return true; + } + default: return false; + } +} + +Dispatcher* PolicyBase::GetDispatcher(int ipc_tag) { + if (ipc_tag >= IPC_LAST_TAG || ipc_tag <= IPC_UNUSED_TAG) + return NULL; + + return ipc_targets_[ipc_tag]; +} + +bool PolicyBase::SetupAllInterceptions(TargetProcess* target) { + InterceptionManager manager(target, relaxed_interceptions_); + + if (policy_) { + for (int i = 0; i < IPC_LAST_TAG; i++) { + if (policy_->entry[i] && !ipc_targets_[i]->SetupService(&manager, i)) + return false; + } + } + + if (!blacklisted_dlls_.empty()) { + std::vector::iterator it = blacklisted_dlls_.begin(); + for (; it != blacklisted_dlls_.end(); ++it) { + manager.AddToUnloadModules(it->c_str()); + } + } + + if (!handle_closer_.SetupHandleInterceptions(&manager)) + return false; + + if (!SetupBasicInterceptions(&manager)) + return false; + + if (!manager.InitializeInterceptions()) + return false; + + // Finally, setup imports on the target so the interceptions can work. + return SetupNtdllImports(target); +} + +bool PolicyBase::SetupHandleCloser(TargetProcess* target) { + return handle_closer_.InitializeTargetHandles(target); +} + +} // namespace sandbox diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h new file mode 100644 index 0000000..b3ea805 --- /dev/null +++ b/sandbox/win/src/sandbox_policy_base.h @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SANDBOX_POLICY_BASE_H_ +#define SANDBOX_SRC_SANDBOX_POLICY_BASE_H_ + +#include + +#include +#include + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/string16.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/handle_closer.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_engine_opcodes.h" +#include "sandbox/src/policy_engine_params.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/win_utils.h" + +namespace sandbox { + +class LowLevelPolicy; +class TargetProcess; +struct PolicyGlobal; + +// We act as a policy dispatcher, implementing the handler for the "ping" IPC, +// so we have to provide the appropriate handler on the OnMessageReady method. +// There is a static_cast for the handler, and the compiler only performs the +// cast if the first base class is Dispatcher. +class PolicyBase : public Dispatcher, public TargetPolicy { + public: + PolicyBase(); + + // TargetPolicy: + virtual void AddRef() OVERRIDE; + virtual void Release() OVERRIDE; + virtual ResultCode SetTokenLevel(TokenLevel initial, + TokenLevel lockdown) OVERRIDE; + virtual ResultCode SetJobLevel(JobLevel job_level, + uint32 ui_exceptions) OVERRIDE; + virtual ResultCode SetAlternateDesktop(bool alternate_winstation) OVERRIDE; + virtual std::wstring GetAlternateDesktop() const OVERRIDE; + virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) OVERRIDE; + virtual void DestroyAlternateDesktop() OVERRIDE; + virtual ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) OVERRIDE; + virtual ResultCode SetDelayedIntegrityLevel( + IntegrityLevel integrity_level) OVERRIDE; + virtual void SetStrictInterceptions() OVERRIDE; + virtual ResultCode AddRule(SubSystem subsystem, Semantics semantics, + const wchar_t* pattern) OVERRIDE; + virtual ResultCode AddDllToUnload(const wchar_t* dll_name); + virtual ResultCode AddKernelObjectToClose(const char16* handle_type, + const char16* handle_name) OVERRIDE; + + // Dispatcher: + virtual Dispatcher* OnMessageReady(IPCParams* ipc, + CallbackGeneric* callback) OVERRIDE; + virtual bool SetupService(InterceptionManager* manager, int service) OVERRIDE; + + // Creates a Job object with the level specified in a previous call to + // SetJobLevel(). Returns the standard windows of ::GetLastError(). + DWORD MakeJobObject(HANDLE* job); + + // Creates the two tokens with the levels specified in a previous call to + // SetTokenLevel(). Returns the standard windows of ::GetLastError(). + DWORD MakeTokens(HANDLE* initial, HANDLE* lockdown); + + // Adds a target process to the internal list of targets. Internally a + // call to TargetProcess::Init() is issued. + bool AddTarget(TargetProcess* target); + + // Called when there are no more active processes in a Job. + // Removes a Job object associated with this policy and the target associated + // with the job. + bool OnJobEmpty(HANDLE job); + + EvalResult EvalPolicy(int service, CountedParameterSetBase* params); + + private: + ~PolicyBase(); + + // Test IPC providers. + bool Ping(IPCInfo* ipc, void* cookie); + + // Returns a dispatcher from ipc_targets_. + Dispatcher* GetDispatcher(int ipc_tag); + + // Sets up interceptions for a new target. + bool SetupAllInterceptions(TargetProcess* target); + + // Sets up the handle closer for a new target. + bool SetupHandleCloser(TargetProcess* target); + + // This lock synchronizes operations on the targets_ collection. + CRITICAL_SECTION lock_; + // Maintains the list of target process associated with this policy. + // The policy takes ownership of them. + typedef std::list TargetSet; + TargetSet targets_; + // Standard object-lifetime reference counter. + volatile LONG ref_count; + // The user-defined global policy settings. + TokenLevel lockdown_level_; + TokenLevel initial_level_; + JobLevel job_level_; + uint32 ui_exceptions_; + bool use_alternate_desktop_; + bool use_alternate_winstation_; + // Helps the file system policy initialization. + bool file_system_init_; + bool relaxed_interceptions_; + IntegrityLevel integrity_level_; + IntegrityLevel delayed_integrity_level_; + // The array of objects that will answer IPC calls. + Dispatcher* ipc_targets_[IPC_LAST_TAG]; + // Object in charge of generating the low level policy. + LowLevelPolicy* policy_maker_; + // Memory structure that stores the low level policy. + PolicyGlobal* policy_; + // The list of dlls to unload in the target process. + std::vector blacklisted_dlls_; + // This is a map of handle-types to names that we need to close in the + // target process. A null set means we need to close all handles of the + // given type. + HandleCloser handle_closer_; + + static HDESK alternate_desktop_handle_; + static HWINSTA alternate_winstation_handle_; + + DISALLOW_COPY_AND_ASSIGN(PolicyBase); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SANDBOX_POLICY_BASE_H_ diff --git a/sandbox/win/src/sandbox_types.h b/sandbox/win/src/sandbox_types.h new file mode 100644 index 0000000..ce9b767 --- /dev/null +++ b/sandbox/win/src/sandbox_types.h @@ -0,0 +1,81 @@ +// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SANDBOX_TYPES_H_ +#define SANDBOX_SRC_SANDBOX_TYPES_H_ + +namespace sandbox { + +// Operation result codes returned by the sandbox API. +enum ResultCode { + SBOX_ALL_OK = 0, + // Error is originating on the win32 layer. Call GetlastError() for more + // information. + SBOX_ERROR_GENERIC = 1, + // An invalid combination of parameters was given to the API. + SBOX_ERROR_BAD_PARAMS = 2, + // The desired operation is not supported at this time. + SBOX_ERROR_UNSUPPORTED = 3, + // The request requires more memory that allocated or available. + SBOX_ERROR_NO_SPACE = 4, + // The ipc service requested does not exist. + SBOX_ERROR_INVALID_IPC = 5, + // The ipc service did not complete. + SBOX_ERROR_FAILED_IPC = 6, + // The requested handle was not found. + SBOX_ERROR_NO_HANDLE = 7, + // This function was not expected to be called at this time. + SBOX_ERROR_UNEXPECTED_CALL = 8, + // WaitForAllTargets is already called. + SBOX_ERROR_WAIT_ALREADY_CALLED = 9, + // A channel error prevented DoCall from executing. + SBOX_ERROR_CHANNEL_ERROR = 10, + // Failed to create the alternate desktop. + SBOX_ERROR_CANNOT_CREATE_DESKTOP = 11, + // Failed to create the alternate window station. + SBOX_ERROR_CANNOT_CREATE_WINSTATION = 12, + // Failed to switch back to the interactive window station. + SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION = 13, + // Placeholder for last item of the enum. + SBOX_ERROR_LAST +}; + +// If the sandbox cannot create a secure environment for the target, the +// target will be forcibly terminated. These are the process exit codes. +enum TerminationCodes { + SBOX_FATAL_INTEGRITY = 7006, // Could not set the integrity level. + SBOX_FATAL_DROPTOKEN = 7007, // Could not lower the token. + SBOX_FATAL_FLUSHANDLES = 7008, // Failed to flush registry handles. + SBOX_FATAL_CACHEDISABLE = 7009, // Failed to forbid HCKU caching. + SBOX_FATAL_CLOSEHANDLES = 7010 // Failed to close pending handles. +}; + +class BrokerServices; +class TargetServices; + +// Contains the pointer to a target or broker service. +struct SandboxInterfaceInfo { + BrokerServices* broker_services; + TargetServices* target_services; +}; + +#if SANDBOX_EXPORTS +#define SANDBOX_INTERCEPT extern "C" __declspec(dllexport) +#else +#define SANDBOX_INTERCEPT extern "C" +#endif + +enum InterceptionType { + INTERCEPTION_INVALID = 0, + INTERCEPTION_SERVICE_CALL, // Trampoline of an NT native call + INTERCEPTION_EAT, + INTERCEPTION_SIDESTEP, // Preamble patch + INTERCEPTION_SMART_SIDESTEP, // Preamble patch but bypass internal calls + INTERCEPTION_UNLOAD_MODULE, // Unload the module (don't patch) + INTERCEPTION_LAST // Placeholder for last item in the enumeration +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SANDBOX_TYPES_H_ diff --git a/sandbox/win/src/sandbox_utils.cc b/sandbox/win/src/sandbox_utils.cc new file mode 100644 index 0000000..3bfa696 --- /dev/null +++ b/sandbox/win/src/sandbox_utils.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/sandbox_utils.h" + +#include + +#include "base/logging.h" +#include "base/win/windows_version.h" +#include "sandbox/src/internal_types.h" +#include "sandbox/src/nt_internals.h" + +namespace sandbox { + +bool GetModuleHandleHelper(DWORD flags, const wchar_t* module_name, + HMODULE* module) { + DCHECK(module); + + HMODULE kernel32_base = ::GetModuleHandle(kKerneldllName); + if (!kernel32_base) { + NOTREACHED(); + return false; + } + + GetModuleHandleExFunction get_module_handle_ex = reinterpret_cast< + GetModuleHandleExFunction>(::GetProcAddress(kernel32_base, + "GetModuleHandleExW")); + if (get_module_handle_ex) { + BOOL ret = get_module_handle_ex(flags, module_name, module); + return (ret ? true : false); + } + + if (!flags) { + *module = ::LoadLibrary(module_name); + } else if (flags & GET_MODULE_HANDLE_EX_FLAG_PIN) { + NOTREACHED(); + return false; + } else if (!(flags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)) { + DCHECK((flags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT) == + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT); + + *module = ::GetModuleHandle(module_name); + } else { + DCHECK((flags & (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)) == + (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)); + + MEMORY_BASIC_INFORMATION info = {0}; + size_t returned = VirtualQuery(module_name, &info, sizeof(info)); + if (sizeof(info) != returned) + return false; + *module = reinterpret_cast(info.AllocationBase); + } + return true; +} + +bool IsXPSP2OrLater() { + base::win::Version version = base::win::GetVersion(); + return (version > base::win::VERSION_XP) || + ((version == base::win::VERSION_XP) && + (base::win::OSInfo::GetInstance()->service_pack().major >= 2)); +} + +void InitObjectAttribs(const std::wstring& name, ULONG attributes, HANDLE root, + OBJECT_ATTRIBUTES* obj_attr, UNICODE_STRING* uni_name) { + static RtlInitUnicodeStringFunction RtlInitUnicodeString; + if (!RtlInitUnicodeString) { + HMODULE ntdll = ::GetModuleHandle(kNtdllName); + RtlInitUnicodeString = reinterpret_cast( + GetProcAddress(ntdll, "RtlInitUnicodeString")); + DCHECK(RtlInitUnicodeString); + } + RtlInitUnicodeString(uni_name, name.c_str()); + InitializeObjectAttributes(obj_attr, uni_name, attributes, root, NULL); +} + +}; // namespace sandbox diff --git a/sandbox/win/src/sandbox_utils.h b/sandbox/win/src/sandbox_utils.h new file mode 100644 index 0000000..314330f --- /dev/null +++ b/sandbox/win/src/sandbox_utils.h @@ -0,0 +1,38 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SANDBOX_UTILS_H__ +#define SANDBOX_SRC_SANDBOX_UTILS_H__ + +#include +#include + +#include "base/basictypes.h" +#include "sandbox/src/nt_internals.h" + +namespace sandbox { + +typedef BOOL (WINAPI* GetModuleHandleExFunction)(DWORD flags, + LPCWSTR module_name, + HMODULE* module); + +// Windows XP provides a nice function in kernel32.dll called GetModuleHandleEx +// This function allows us to verify if a function exported by the module +// lies in the module itself. +// As we need compatibility with windows 2000, we cannot use this function +// by calling it by name. This helper function checks if the GetModuleHandleEx +// function is exported by kernel32 and uses it, otherwise, implemets part of +// the functionality exposed by GetModuleHandleEx. +bool GetModuleHandleHelper(DWORD flags, const wchar_t* module_name, + HMODULE* module); + +// Returns true if the current OS is Windows XP SP2 or later. +bool IsXPSP2OrLater(); + +void InitObjectAttribs(const std::wstring& name, ULONG attributes, HANDLE root, + OBJECT_ATTRIBUTES* obj_attr, UNICODE_STRING* uni_name); + +}; // namespace sandbox + +#endif // SANDBOX_SRC_SANDBOX_UTILS_H__ diff --git a/sandbox/win/src/security_level.h b/sandbox/win/src/security_level.h new file mode 100644 index 0000000..467f96f --- /dev/null +++ b/sandbox/win/src/security_level.h @@ -0,0 +1,127 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SECURITY_LEVEL_H_ +#define SANDBOX_SRC_SECURITY_LEVEL_H_ + +namespace sandbox { + +// List of all the integrity levels supported in the sandbox. This is used +// only on Windows Vista. You can't set the integrity level of the process +// in the sandbox to a level higher than yours. +enum IntegrityLevel { + INTEGRITY_LEVEL_SYSTEM, + INTEGRITY_LEVEL_HIGH, + INTEGRITY_LEVEL_MEDIUM, + INTEGRITY_LEVEL_MEDIUM_LOW, + INTEGRITY_LEVEL_LOW, + INTEGRITY_LEVEL_BELOW_LOW, + INTEGRITY_LEVEL_UNTRUSTED, + INTEGRITY_LEVEL_LAST +}; + +// The Token level specifies a set of security profiles designed to +// provide the bulk of the security of sandbox. +// +// TokenLevel |Restricting |Deny Only |Privileges| +// |Sids |Sids | | +// ----------------------------|--------------|----------------|----------| +// USER_LOCKDOWN | Null Sid | All | None | +// ----------------------------|--------------|----------------|----------| +// USER_RESTRICTED | RESTRICTED | All | Traverse | +// ----------------------------|--------------|----------------|----------| +// USER_LIMITED | Users | All except: | Traverse | +// | Everyone | Users | | +// | RESTRICTED | Everyone | | +// | | Interactive | | +// ----------------------------|--------------|----------------|----------| +// USER_INTERACTIVE | Users | All except: | Traverse | +// | Everyone | Users | | +// | RESTRICTED | Everyone | | +// | Owner | Interactive | | +// | | Local | | +// | | Authent-users | | +// | | User | | +// ----------------------------|--------------|----------------|----------| +// USER_NON_ADMIN | None | All except: | Traverse | +// | | Users | | +// | | Everyone | | +// | | Interactive | | +// | | Local | | +// | | Authent-users | | +// | | User | | +// ----------------------------|--------------|----------------|----------| +// USER_RESTRICTED_SAME_ACCESS | All | None | All | +// ----------------------------|--------------|----------------|----------| +// USER_UNPROTECTED | None | None | All | +// ----------------------------|--------------|----------------|----------| +// +// The above restrictions are actually a transformation that is applied to +// the existing broker process token. The resulting token that will be +// applied to the target process depends both on the token level selected +// and on the broker token itself. +// +// The LOCKDOWN and RESTRICTED are designed to allow access to almost +// nothing that has security associated with and they are the recommended +// levels to run sandboxed code specially if there is a chance that the +// broker is process might be started by a user that belongs to the Admins +// or power users groups. +enum TokenLevel { + USER_LOCKDOWN = 0, + USER_RESTRICTED, + USER_LIMITED, + USER_INTERACTIVE, + USER_NON_ADMIN, + USER_RESTRICTED_SAME_ACCESS, + USER_UNPROTECTED +}; + +// The Job level specifies a set of decreasing security profiles for the +// Job object that the target process will be placed into. +// This table summarizes the security associated with each level: +// +// JobLevel |General |Quota | +// |restrictions |restrictions | +// -----------------|---------------------------------- |--------------------| +// JOB_UNPROTECTED | None | *Kill on Job close.| +// -----------------|---------------------------------- |--------------------| +// JOB_INTERACTIVE | *Forbid system-wide changes using | | +// | SystemParametersInfo(). | *Kill on Job close.| +// | *Forbid the creation/switch of | | +// | Desktops. | | +// | *Forbids calls to ExitWindows(). | | +// -----------------|---------------------------------- |--------------------| +// JOB_LIMITED_USER | Same as INTERACTIVE_USER plus: | *One active process| +// | *Forbid changes to the display | limit. | +// | settings. | *Kill on Job close.| +// -----------------|---------------------------------- |--------------------| +// JOB_RESTRICTED | Same as LIMITED_USER plus: | *One active process| +// | * No read/write to the clipboard. | limit. | +// | * No access to User Handles that | *Kill on Job close.| +// | belong to other processes. | | +// | * Forbid message broadcasts. | | +// | * Forbid setting global hooks. | | +// | * No access to the global atoms | | +// | table. | | +// -----------------|-----------------------------------|--------------------| +// JOB_LOCKDOWN | Same as RESTRICTED | *One active process| +// | | limit. | +// | | *Kill on Job close.| +// | | *Kill on unhandled | +// | | exception. | +// | | | +// In the context of the above table, 'user handles' refers to the handles of +// windows, bitmaps, menus, etc. Files, treads and registry handles are kernel +// handles and are not affected by the job level settings. +enum JobLevel { + JOB_LOCKDOWN = 0, + JOB_RESTRICTED, + JOB_LIMITED_USER, + JOB_INTERACTIVE, + JOB_UNPROTECTED +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SECURITY_LEVEL_H_ diff --git a/sandbox/win/src/service_resolver.cc b/sandbox/win/src/service_resolver.cc new file mode 100644 index 0000000..79579a0 --- /dev/null +++ b/sandbox/win/src/service_resolver.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/service_resolver.h" + +#include "base/logging.h" +#include "base/win/pe_image.h" + +namespace sandbox { + +NTSTATUS ServiceResolverThunk::ResolveInterceptor( + const void* interceptor_module, + const char* interceptor_name, + const void** address) { + // After all, we are using a locally mapped version of the exe, so the + // action is the same as for a target function. + return ResolveTarget(interceptor_module, interceptor_name, + const_cast(address)); +} + +// In this case all the work is done from the parent, so resolve is +// just a simple GetProcAddress. +NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module, + const char* function_name, + void** address) { + DCHECK(address); + if (NULL == module) + return STATUS_UNSUCCESSFUL; + + base::win::PEImage module_image(module); + *address = module_image.GetProcAddress(function_name); + + if (NULL == *address) { + NOTREACHED(); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +} // namespace sandbox diff --git a/sandbox/win/src/service_resolver.h b/sandbox/win/src/service_resolver.h new file mode 100644 index 0000000..99eadb6 --- /dev/null +++ b/sandbox/win/src/service_resolver.h @@ -0,0 +1,148 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SERVICE_RESOLVER_H__ +#define SANDBOX_SRC_SERVICE_RESOLVER_H__ + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/resolver.h" + +namespace sandbox { + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll. +class ServiceResolverThunk : public ResolverThunk { + public: + // The service resolver needs a child process to write to. + ServiceResolverThunk(HANDLE process, bool relaxed) + : process_(process), ntdll_base_(NULL), win2k_(false), + relaxed_(relaxed), relative_jump_(0) {} + virtual ~ServiceResolverThunk() {} + + // Implementation of Resolver::Setup. + virtual NTSTATUS Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used); + + // Implementation of Resolver::ResolveInterceptor. + virtual NTSTATUS ResolveInterceptor(const void* module, + const char* function_name, + const void** address); + + // Implementation of Resolver::ResolveTarget. + virtual NTSTATUS ResolveTarget(const void* module, + const char* function_name, + void** address); + + // Implementation of Resolver::GetThunkSize. + virtual size_t GetThunkSize() const; + + protected: + // The unit test will use this member to allow local patch on a buffer. + HMODULE ntdll_base_; + + // Handle of the child process. + HANDLE process_; + + protected: + // Keeps track of a Windows 2000 resolver. + bool win2k_; + + private: + // Returns true if the code pointer by target_ corresponds to the expected + // type of function. Saves that code on the first part of the thunk pointed + // by local_thunk (should be directly accessible from the parent). + virtual bool IsFunctionAService(void* local_thunk) const; + + // Performs the actual patch of target_. + // local_thunk must be already fully initialized, and the first part must + // contain the original code. The real type of this buffer is ServiceFullThunk + // (yes, private). remote_thunk (real type ServiceFullThunk), must be + // allocated on the child, and will contain the thunk data, after this call. + // Returns the apropriate status code. + virtual NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk); + + // Provides basically the same functionality as IsFunctionAService but it + // continues even if it does not recognize the function code. remote_thunk + // is the address of our memory on the child. + bool SaveOriginalFunction(void* local_thunk, void* remote_thunk); + + // true if we are allowed to patch already-patched functions. + bool relaxed_; + ULONG relative_jump_; + + DISALLOW_COPY_AND_ASSIGN(ServiceResolverThunk); +}; + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll on WOW64 (32 bit ntdll on 64 bit Vista). +class Wow64ResolverThunk : public ServiceResolverThunk { + public: + // The service resolver needs a child process to write to. + Wow64ResolverThunk(HANDLE process, bool relaxed) + : ServiceResolverThunk(process, relaxed) {} + virtual ~Wow64ResolverThunk() {} + + private: + virtual bool IsFunctionAService(void* local_thunk) const; + + DISALLOW_COPY_AND_ASSIGN(Wow64ResolverThunk); +}; + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll on WOW64 for Windows 8. +class Wow64W8ResolverThunk : public ServiceResolverThunk { + public: + // The service resolver needs a child process to write to. + Wow64W8ResolverThunk(HANDLE process, bool relaxed) + : ServiceResolverThunk(process, relaxed) {} + virtual ~Wow64W8ResolverThunk() {} + + private: + virtual bool IsFunctionAService(void* local_thunk) const; + + DISALLOW_COPY_AND_ASSIGN(Wow64W8ResolverThunk); +}; + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll on Windows 2000 and XP pre SP2. +class Win2kResolverThunk : public ServiceResolverThunk { + public: + // The service resolver needs a child process to write to. + Win2kResolverThunk(HANDLE process, bool relaxed) + : ServiceResolverThunk(process, relaxed) { + win2k_ = true; + } + virtual ~Win2kResolverThunk() {} + + private: + virtual bool IsFunctionAService(void* local_thunk) const; + + DISALLOW_COPY_AND_ASSIGN(Win2kResolverThunk); +}; + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll on Windows 8. +class Win8ResolverThunk : public ServiceResolverThunk { + public: + // The service resolver needs a child process to write to. + Win8ResolverThunk(HANDLE process, bool relaxed) + : ServiceResolverThunk(process, relaxed) {} + virtual ~Win8ResolverThunk() {} + + private: + virtual bool IsFunctionAService(void* local_thunk) const; + + DISALLOW_COPY_AND_ASSIGN(Win8ResolverThunk); +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_SERVICE_RESOLVER_H__ diff --git a/sandbox/win/src/service_resolver_32.cc b/sandbox/win/src/service_resolver_32.cc new file mode 100644 index 0000000..2f5597b --- /dev/null +++ b/sandbox/win/src/service_resolver_32.cc @@ -0,0 +1,424 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/service_resolver.h" + +#include "base/memory/scoped_ptr.h" +#include "sandbox/src/sandbox_utils.h" +#include "sandbox/src/win_utils.h" + +namespace { +#pragma pack(push, 1) + +const BYTE kMovEax = 0xB8; +const BYTE kMovEdx = 0xBA; +const USHORT kMovEdxEsp = 0xD48B; +const USHORT kCallPtrEdx = 0x12FF; +const USHORT kCallEdx = 0xD2FF; +const BYTE kCallEip = 0xE8; +const BYTE kRet = 0xC2; +const BYTE kRet2 = 0xC3; +const BYTE kNop = 0x90; +const USHORT kJmpEdx = 0xE2FF; +const USHORT kXorEcx = 0xC933; +const ULONG kLeaEdx = 0x0424548D; +const ULONG kCallFs1 = 0xC015FF64; +const USHORT kCallFs2 = 0; +const BYTE kCallFs3 = 0; +const BYTE kAddEsp1 = 0x83; +const USHORT kAddEsp2 = 0x4C4; +const BYTE kJmp32 = 0xE9; +const USHORT kSysenter = 0x340F; + +const int kMaxService = 1000; + +// Service code for 32 bit systems. +// NOTE: on win2003 "call dword ptr [edx]" is "call edx". +struct ServiceEntry { + // This struct contains roughly the following code: + // 00 mov eax,25h + // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300) + // 0a call dword ptr [edx] + // 0c ret 2Ch + // 0f nop + BYTE mov_eax; // = B8 + ULONG service_id; + BYTE mov_edx; // = BA + ULONG stub; + USHORT call_ptr_edx; // = FF 12 + BYTE ret; // = C2 + USHORT num_params; + BYTE nop; +}; + +// Service code for 32 bit Windows 8. +struct ServiceEntryW8 { + // This struct contains the following code: + // 00 b825000000 mov eax,25h + // 05 e803000000 call eip+3 + // 0a c22c00 ret 2Ch + // 0d 8bd4 mov edx,esp + // 0f 0f34 sysenter + // 11 c3 ret + // 12 8bff mov edi,edi + BYTE mov_eax; // = B8 + ULONG service_id; + BYTE call_eip; // = E8 + ULONG call_offset; + BYTE ret_p; // = C2 + USHORT num_params; + USHORT mov_edx_esp; // = BD D4 + USHORT sysenter; // = 0F 34 + BYTE ret; // = C3 + USHORT nop; +}; + +// Service code for a 32 bit process running on a 64 bit os. +struct Wow64Entry { + // This struct may contain one of two versions of code: + // 1. For XP, Vista and 2K3: + // 00 b825000000 mov eax, 25h + // 05 33c9 xor ecx, ecx + // 07 8d542404 lea edx, [esp + 4] + // 0b 64ff15c0000000 call dword ptr fs:[0C0h] + // 12 c22c00 ret 2Ch + // + // 2. For Windows 7: + // 00 b825000000 mov eax, 25h + // 05 33c9 xor ecx, ecx + // 07 8d542404 lea edx, [esp + 4] + // 0b 64ff15c0000000 call dword ptr fs:[0C0h] + // 12 83c404 add esp, 4 + // 15 c22c00 ret 2Ch + // + // So we base the structure on the bigger one: + BYTE mov_eax; // = B8 + ULONG service_id; + USHORT xor_ecx; // = 33 C9 + ULONG lea_edx; // = 8D 54 24 04 + ULONG call_fs1; // = 64 FF 15 C0 + USHORT call_fs2; // = 00 00 + BYTE call_fs3; // = 00 + BYTE add_esp1; // = 83 or ret + USHORT add_esp2; // = C4 04 or num_params + BYTE ret; // = C2 + USHORT num_params; +}; + +// Service code for a 32 bit process running on 64 bit Windows 8. +struct Wow64EntryW8 { + // 00 b825000000 mov eax, 25h + // 05 64ff15c0000000 call dword ptr fs:[0C0h] + // 0b c22c00 ret 2Ch + // 0f 90 nop + BYTE mov_eax; // = B8 + ULONG service_id; + ULONG call_fs1; // = 64 FF 15 C0 + USHORT call_fs2; // = 00 00 + BYTE call_fs3; // = 00 + BYTE ret; // = C2 + USHORT num_params; + BYTE nop; +}; + +// Make sure that relaxed patching works as expected. +const size_t kMinServiceSize = offsetof(ServiceEntry, ret); +COMPILE_ASSERT(sizeof(ServiceEntryW8) >= kMinServiceSize, wrong_service_len); +COMPILE_ASSERT(sizeof(Wow64Entry) >= kMinServiceSize, wrong_service_len); +COMPILE_ASSERT(sizeof(Wow64EntryW8) >= kMinServiceSize, wrong_service_len); + +struct ServiceFullThunk { + union { + ServiceEntry original; + ServiceEntryW8 original_w8; + Wow64Entry wow_64; + Wow64EntryW8 wow_64_w8; + }; + int internal_thunk; // Dummy member to the beginning of the internal thunk. +}; + +#pragma pack(pop) + +}; // namespace + +namespace sandbox { + +NTSTATUS ServiceResolverThunk::Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used) { + NTSTATUS ret = Init(target_module, interceptor_module, target_name, + interceptor_name, interceptor_entry_point, + thunk_storage, storage_bytes); + if (!NT_SUCCESS(ret)) + return ret; + + relative_jump_ = 0; + size_t thunk_bytes = GetThunkSize(); + scoped_array thunk_buffer(new char[thunk_bytes]); + ServiceFullThunk* thunk = reinterpret_cast( + thunk_buffer.get()); + + if (!IsFunctionAService(&thunk->original) && + (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) + return STATUS_UNSUCCESSFUL; + + ret = PerformPatch(thunk, thunk_storage); + + if (NULL != storage_used) + *storage_used = thunk_bytes; + + return ret; +} + +size_t ServiceResolverThunk::GetThunkSize() const { + return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize(); +} + +bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { + ServiceEntry function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) + return false; + + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || + kMovEdx != function_code.mov_edx || + (kCallPtrEdx != function_code.call_ptr_edx && + kCallEdx != function_code.call_ptr_edx) || + kRet != function_code.ret) + return false; + + // Find the system call pointer if we don't already have it. + if (kCallEdx != function_code.call_ptr_edx) { + DWORD ki_system_call; + if (!::ReadProcessMemory(process_, + bit_cast(function_code.stub), + &ki_system_call, sizeof(ki_system_call), &read)) + return false; + + if (sizeof(ki_system_call) != read) + return false; + + HMODULE module_1, module_2; + // last check, call_stub should point to a KiXXSystemCall function on ntdll + if (!GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + bit_cast(ki_system_call), + &module_1)) + return false; + + if (NULL != ntdll_base_) { + // This path is only taken when running the unit tests. We want to be + // able to patch a buffer in memory, so target_ is not inside ntdll. + module_2 = ntdll_base_; + } else { + if (!GetModuleHandleHelper( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(target_), + &module_2)) { + return false; + } + } + + if (module_1 != module_2) + return false; + } + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + + return true; +} + +NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, + void* remote_thunk) { + ServiceEntry intercepted_code; + size_t bytes_to_write = sizeof(intercepted_code); + ServiceFullThunk *full_local_thunk = reinterpret_cast( + local_thunk); + ServiceFullThunk *full_remote_thunk = reinterpret_cast( + remote_thunk); + + // patch the original code + memcpy(&intercepted_code, &full_local_thunk->original, + sizeof(intercepted_code)); + intercepted_code.mov_eax = kMovEax; + intercepted_code.service_id = full_local_thunk->original.service_id; + intercepted_code.mov_edx = kMovEdx; + intercepted_code.stub = bit_cast(&full_remote_thunk->internal_thunk); + intercepted_code.call_ptr_edx = kJmpEdx; + bytes_to_write = kMinServiceSize; + + if (relative_jump_) { + intercepted_code.mov_eax = kJmp32; + intercepted_code.service_id = relative_jump_; + bytes_to_write = offsetof(ServiceEntry, mov_edx); + } + + // setup the thunk + SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(), + remote_thunk, interceptor_); + + size_t thunk_size = GetThunkSize(); + + // copy the local thunk buffer to the child + SIZE_T written; + if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, + thunk_size, &written)) + return STATUS_UNSUCCESSFUL; + + if (thunk_size != written) + return STATUS_UNSUCCESSFUL; + + // and now change the function to intercept, on the child + if (NULL != ntdll_base_) { + // running a unit test + if (!::WriteProcessMemory(process_, target_, &intercepted_code, + bytes_to_write, &written)) + return STATUS_UNSUCCESSFUL; + } else { + if (!WriteProtectedChildMemory(process_, target_, &intercepted_code, + bytes_to_write)) + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk, + void* remote_thunk) { + ServiceEntry function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) + return false; + + if (sizeof(function_code) != read) + return false; + + if (kJmp32 == function_code.mov_eax) { + // Plain old entry point patch. The relative jump address follows it. + ULONG relative = function_code.service_id; + + // First, fix our copy of their patch. + relative += bit_cast(target_) - bit_cast(remote_thunk); + + function_code.service_id = relative; + + // And now, remember how to re-patch it. + ServiceFullThunk *full_thunk = + reinterpret_cast(remote_thunk); + + const ULONG kJmp32Size = 5; + + relative_jump_ = bit_cast(&full_thunk->internal_thunk) - + bit_cast(target_) - kJmp32Size; + } + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + + return true; +} + +bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { + Wow64Entry function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) + return false; + + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx || + kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 || + kCallFs2 != function_code.call_fs2 || kCallFs3 != function_code.call_fs3) + return false; + + if ((kAddEsp1 == function_code.add_esp1 && + kAddEsp2 == function_code.add_esp2 && + kRet == function_code.ret) || kRet == function_code.add_esp1) { + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + return true; + } + + return false; +} + +bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const { + Wow64EntryW8 function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) + return false; + + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 || + kCallFs2 != function_code.call_fs2 || + kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) { + return false; + } + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + return true; +} + +bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const { + ServiceEntry function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) + return false; + + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || + function_code.service_id > kMaxService) + return false; + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + + return true; +} + +bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const { + ServiceEntryW8 function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) + return false; + + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip || + function_code.call_offset != 3 || kRet != function_code.ret_p || + kMovEdxEsp != function_code.mov_edx_esp || + kSysenter != function_code.sysenter || kRet2 != function_code.ret) { + return false; + } + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/service_resolver_64.cc b/sandbox/win/src/service_resolver_64.cc new file mode 100644 index 0000000..01e3b1a --- /dev/null +++ b/sandbox/win/src/service_resolver_64.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/service_resolver.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "sandbox/src/win_utils.h" + +namespace { +#pragma pack(push, 1) + +const ULONG kMmovR10EcxMovEax = 0xB8D18B4C; +const USHORT kSyscall = 0x050F; +const BYTE kRetNp = 0xC3; +const ULONG64 kMov1 = 0x54894808244C8948; +const ULONG64 kMov2 = 0x4C182444894C1024; +const ULONG kMov3 = 0x20244C89; + +// Service code for 64 bit systems. +struct ServiceEntry { + // This struct contains roughly the following code: + // 00 mov r10,rcx + // 03 mov eax,52h + // 08 syscall + // 0a ret + // 0b xchg ax,ax + // 0e xchg ax,ax + + ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8 + ULONG service_id; + USHORT syscall; // = 0F 05 + BYTE ret; // = C3 + BYTE pad; // = 66 + USHORT xchg_ax_ax1; // = 66 90 + USHORT xchg_ax_ax2; // = 66 90 +}; + +// Service code for 64 bit Windows 8. +struct ServiceEntryW8 { + // This struct contains the following code: + // 00 48894c2408 mov [rsp+8], rcx + // 05 4889542410 mov [rsp+10], rdx + // 0a 4c89442418 mov [rsp+18], r8 + // 0f 4c894c2420 mov [rsp+20], r9 + // 14 4c8bd1 mov r10,rcx + // 17 b825000000 mov eax,25h + // 1c 0f05 syscall + // 1e c3 ret + // 1f 90 nop + + ULONG64 mov_1; // = 48 89 4C 24 08 48 89 54 + ULONG64 mov_2; // = 24 10 4C 89 44 24 18 4C + ULONG mov_3; // = 89 4C 24 20 + ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8 + ULONG service_id; + USHORT syscall; // = 0F 05 + BYTE ret; // = C2 + BYTE nop; // = 90 +}; + +// We don't have an internal thunk for x64. +struct ServiceFullThunk { + union { + ServiceEntry original; + ServiceEntryW8 original_w8; + }; +}; + +#pragma pack(pop) + +bool IsService(const void* source) { + const ServiceEntry* service = + reinterpret_cast(source); + + return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax && + kSyscall == service->syscall && kRetNp == service->ret); +} + +}; // namespace + +namespace sandbox { + +NTSTATUS ServiceResolverThunk::Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used) { + NTSTATUS ret = Init(target_module, interceptor_module, target_name, + interceptor_name, interceptor_entry_point, + thunk_storage, storage_bytes); + if (!NT_SUCCESS(ret)) + return ret; + + size_t thunk_bytes = GetThunkSize(); + scoped_array thunk_buffer(new char[thunk_bytes]); + ServiceFullThunk* thunk = reinterpret_cast( + thunk_buffer.get()); + + if (!IsFunctionAService(&thunk->original)) + return STATUS_UNSUCCESSFUL; + + ret = PerformPatch(thunk, thunk_storage); + + if (NULL != storage_used) + *storage_used = thunk_bytes; + + return ret; +} + +size_t ServiceResolverThunk::GetThunkSize() const { + return sizeof(ServiceFullThunk); +} + +bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { + ServiceFullThunk function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) + return false; + + if (sizeof(function_code) != read) + return false; + + if (!IsService(&function_code)) { + // See if it's the Win8 signature. + ServiceEntryW8* w8_service = &function_code.original_w8; + if (!IsService(&w8_service->mov_r10_rcx_mov_eax) || + w8_service->mov_1 != kMov1 || w8_service->mov_1 != kMov1 || + w8_service->mov_1 != kMov1) { + return false; + } + } + + // Save the verified code. + memcpy(local_thunk, &function_code, sizeof(function_code)); + + return true; +} + +NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, + void* remote_thunk) { + ServiceFullThunk* full_local_thunk = reinterpret_cast( + local_thunk); + ServiceFullThunk* full_remote_thunk = reinterpret_cast( + remote_thunk); + + // Patch the original code. + ServiceEntry local_service; + DCHECK_GE(GetInternalThunkSize(), sizeof(local_service)); + if (!SetInternalThunk(&local_service, sizeof(local_service), NULL, + interceptor_)) + return STATUS_UNSUCCESSFUL; + + // Copy the local thunk buffer to the child. + SIZE_T actual; + if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, + sizeof(ServiceFullThunk), &actual)) + return STATUS_UNSUCCESSFUL; + + if (sizeof(ServiceFullThunk) != actual) + return STATUS_UNSUCCESSFUL; + + // And now change the function to intercept, on the child. + if (NULL != ntdll_base_) { + // Running a unit test. + if (!::WriteProcessMemory(process_, target_, &local_service, + sizeof(local_service), &actual)) + return STATUS_UNSUCCESSFUL; + } else { + if (!WriteProtectedChildMemory(process_, target_, &local_service, + sizeof(local_service))) + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { + NOTREACHED(); + return false; +} + +bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const { + NOTREACHED(); + return false; +} + +} // namespace sandbox diff --git a/sandbox/win/src/service_resolver_unittest.cc b/sandbox/win/src/service_resolver_unittest.cc new file mode 100644 index 0000000..fc85c86 --- /dev/null +++ b/sandbox/win/src/service_resolver_unittest.cc @@ -0,0 +1,227 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains unit tests for ServiceResolverThunk. + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/win/windows_version.h" +#include "sandbox/src/resolver.h" +#include "sandbox/src/sandbox_utils.h" +#include "sandbox/src/service_resolver.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll. +template +class ResolverThunkTest : public T { + public: + // The service resolver needs a child process to write to. + explicit ResolverThunkTest(bool relaxed) + : T(::GetCurrentProcess(), relaxed) {} + + // Sets the interception target to the desired address. + void set_target(void* target) { + fake_target_ = target; + } + + protected: + // Overrides Resolver::Init + virtual NTSTATUS Init(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes) { + NTSTATUS ret = STATUS_SUCCESS; + ret = ResolverThunk::Init(target_module, interceptor_module, target_name, + interceptor_name, interceptor_entry_point, + thunk_storage, storage_bytes); + EXPECT_EQ(STATUS_SUCCESS, ret); + + target_ = fake_target_; + ntdll_base_ = ::GetModuleHandle(L"ntdll.dll"); + return ret; + }; + + private: + // Holds the address of the fake target. + void* fake_target_; + + DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest); +}; + +typedef ResolverThunkTest WinXpResolverTest; + +#if !defined(_WIN64) +typedef ResolverThunkTest Win2kResolverTest; +typedef ResolverThunkTest Win8ResolverTest; +typedef ResolverThunkTest Wow64ResolverTest; +typedef ResolverThunkTest Wow64W8ResolverTest; +#endif + +const BYTE kJump32 = 0xE9; + +void CheckJump(void* source, void* target) { +#pragma pack(push) +#pragma pack(1) + struct Code { + BYTE jump; + ULONG delta; + }; +#pragma pack(pop) + +#if defined(_WIN64) + FAIL() << "Running 32-bit codepath"; +#else + Code* patched = reinterpret_cast(source); + EXPECT_EQ(kJump32, patched->jump); + + ULONG source_addr = bit_cast(source); + ULONG target_addr = bit_cast(target); + EXPECT_EQ(target_addr + 19 - source_addr, patched->delta); +#endif +} + +NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, + sandbox::ServiceResolverThunk* resolver) { + HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); + EXPECT_TRUE(NULL != ntdll_base); + + void* target = ::GetProcAddress(ntdll_base, function); + EXPECT_TRUE(NULL != target); + if (NULL == target) + return STATUS_UNSUCCESSFUL; + + BYTE service[50]; + memcpy(service, target, sizeof(service)); + + static_cast(resolver)->set_target(service); + + // Any pointer will do as an interception_entry_point + void* function_entry = resolver; + size_t thunk_size = resolver->GetThunkSize(); + scoped_array thunk(new char[thunk_size]); + size_t used; + + NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, + function_entry, thunk.get(), thunk_size, + &used); + if (NT_SUCCESS(ret)) { + EXPECT_EQ(thunk_size, used); + EXPECT_NE(0, memcmp(service, target, sizeof(service))); + EXPECT_NE(kJump32, service[0]); + + if (relaxed) { + // It's already patched, let's patch again, and simulate a direct patch. + service[0] = kJump32; + ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, + thunk.get(), thunk_size, &used); + CheckJump(service, thunk.get()); + } + } + + return ret; +} + +sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) { +#if defined(_WIN64) + return new WinXpResolverTest(relaxed); +#else + base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); + if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { + if (os_info->version() >= base::win::VERSION_WIN8) + return new Wow64W8ResolverTest(relaxed); + return new Wow64ResolverTest(relaxed); + } + + if (!sandbox::IsXPSP2OrLater()) + return new Win2kResolverTest(relaxed); + + if (os_info->version() >= base::win::VERSION_WIN8) + return new Win8ResolverTest(relaxed); + + return new WinXpResolverTest(relaxed); +#endif +} + +NTSTATUS PatchNtdll(const char* function, bool relaxed) { + sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed); + + NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver); + delete resolver; + return ret; +} + +TEST(ServiceResolverTest, PatchesServices) { + NTSTATUS ret = PatchNtdll("NtClose", false); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); + + ret = PatchNtdll("NtCreateFile", false); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << + ::GetLastError(); + + ret = PatchNtdll("NtCreateMutant", false); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << + ::GetLastError(); + + ret = PatchNtdll("NtMapViewOfSection", false); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << + ::GetLastError(); +} + +TEST(ServiceResolverTest, FailsIfNotService) { +#if !defined(_WIN64) + EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false)); +#endif + + EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false)); +} + +TEST(ServiceResolverTest, PatchesPatchedServices) { +// We don't support "relaxed mode" for Win64 apps. +#if !defined(_WIN64) + NTSTATUS ret = PatchNtdll("NtClose", true); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); + + ret = PatchNtdll("NtCreateFile", true); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << + ::GetLastError(); + + ret = PatchNtdll("NtCreateMutant", true); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << + ::GetLastError(); + + ret = PatchNtdll("NtMapViewOfSection", true); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << + ::GetLastError(); +#endif +} + +TEST(ServiceResolverTest, MultiplePatchedServices) { +// We don't support "relaxed mode" for Win64 apps. +#if !defined(_WIN64) + sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); + NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); + + ret = PatchNtdllWithResolver("NtCreateFile", true, resolver); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << + ::GetLastError(); + + ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << + ::GetLastError(); + + ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver); + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << + ::GetLastError(); + delete resolver; +#endif +} + +} // namespace diff --git a/sandbox/win/src/shared_handles.cc b/sandbox/win/src/shared_handles.cc new file mode 100644 index 0000000..d07f057 --- /dev/null +++ b/sandbox/win/src/shared_handles.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/shared_handles.h" + +namespace sandbox { + +// Note once again the the assumption here is that the shared memory is +// initialized with zeros in the process that calls SetHandle and that +// the process that calls GetHandle 'sees' this memory. + +SharedHandles::SharedHandles() { + shared_.items = NULL; + shared_.max_items = 0; +} + +bool SharedHandles::Init(void* raw_mem, size_t size_bytes) { + if (size_bytes < sizeof(shared_.items[0])) { + // The shared memory is too small! + return false; + } + shared_.items = static_cast(raw_mem); + shared_.max_items = size_bytes / sizeof(shared_.items[0]); + return true; +} + +// Note that an empty slot is marked with a tag == 0 that is why is +// not a valid imput tag +bool SharedHandles::SetHandle(uint32 tag, HANDLE handle) { + if (0 == tag) { + // Invalid tag + return false; + } + // Find empty slot and put the tag and the handle there + SharedItem* empty_slot = FindByTag(0); + if (NULL == empty_slot) { + return false; + } + empty_slot->tag = tag; + empty_slot->item = handle; + return true; +} + +bool SharedHandles::GetHandle(uint32 tag, HANDLE* handle) { + if (0 == tag) { + // Invalid tag + return false; + } + SharedItem* found = FindByTag(tag); + if (NULL == found) { + return false; + } + *handle = found->item; + return true; +} + +SharedHandles::SharedItem* SharedHandles::FindByTag(uint32 tag) { + for (size_t ix = 0; ix != shared_.max_items; ++ix) { + if (tag == shared_.items[ix].tag) { + return &shared_.items[ix]; + } + } + return NULL; +} + +} // namespace sandbox diff --git a/sandbox/win/src/shared_handles.h b/sandbox/win/src/shared_handles.h new file mode 100644 index 0000000..2c76bfb --- /dev/null +++ b/sandbox/win/src/shared_handles.h @@ -0,0 +1,108 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SHARED_HANDLES_H__ +#define SANDBOX_SRC_SHARED_HANDLES_H__ + +#include "base/basictypes.h" + +#ifndef HANDLE +// We can provide our own windows compatilble handle definition, but +// in general we want to rely on the client of this api to include +// the proper windows headers. Note that we don't want to bring the +// whole into scope if we don't have to. +typedef void* HANDLE; +#endif + +namespace sandbox { + +// SharedHandles is a simple class to stash and find windows object handles +// given a raw block of memory which is shared between two processes. +// It addresses the need to communicate a handle value between two windows +// processes given that they are already sharing some memory. +// +// This class is not exposed directly to users of the sanbox API, instead +// we expose the wrapper methods TargetProcess::TransferHandle( ) and +// TargetServices::GetTransferHandle() +// +// Use it for a small number of items, since internaly uses linear seach +// +// The use is very simple. Given a shared memory between proces A and B: +// process A: +// HANDLE handle = SomeFunction(..); +// SharedHandles shared_handes; +// shared_handles.Init(memory) +// shared_handles.SetHandle(3, handle); +// +// process B: +// SharedHandles shared_handes; +// shared_handles.Init(memory) +// HANDLE handle = shared_handles.GetHandle(3); +// +// Note that '3' in this example is a unique id, that must be agreed before +// transfer +// +// Note2: While this class can be used in a single process, there are +// better alternatives such as STL +// +// Note3: Under windows a kernel object handle in one process does not +// make sense for another process unless there is a DuplicateHandle( ) +// call involved which this class DOES NOT do that for you. +// +// Note4: Under windows, shared memory when created is initialized to +// zeros always. If you are not using shared memory it is your responsability +// to zero it for the setter process and to copy it to the getter process. +class SharedHandles { + public: + SharedHandles(); + + // Initializes the shared memory for use. + // Pass the shared memory base and size. It will internally compute + // how many handles can it store. If initialization fails the return value + // is false. + bool Init(void* raw_mem, size_t size_bytes); + + // Sets a handle in the shared memory for transfer. + // Parameters: + // tag : an integer, different from zero that uniquely identfies the + // handle to transfer. + // handle: the handle value associated with 'tag' to tranfer + // Returns false if there is not enough space in the shared memory for + // this handle. + bool SetHandle(uint32 tag, HANDLE handle); + + // Gets a handle previously stored by SetHandle. + // Parameters: + // tag: an integer different from zero that uniquely identfies the handle + // to retrieve. + // *handle: output handle value if the call was succesful. + // If a handle with the provided tag is not found the return value is false. + // If the tag is found the return value is true. + bool GetHandle(uint32 tag, HANDLE* handle); + + private: + // A single item is the tuple handle/tag + struct SharedItem { + uint32 tag; + void* item; + }; + + // SharedMem is used to layout the memory as an array of SharedItems + struct SharedMem { + size_t max_items; + SharedItem* items; + }; + + // Finds an Item tuple provided the handle tag. + // Uses linear search because we expect the number of handles to be + // small (say less than ~100). + SharedItem* FindByTag(uint32 tag); + + SharedMem shared_; + DISALLOW_COPY_AND_ASSIGN(SharedHandles); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SHARED_HANDLES_H__ diff --git a/sandbox/win/src/sharedmem_ipc_client.cc b/sandbox/win/src/sharedmem_ipc_client.cc new file mode 100644 index 0000000..30c03b9 --- /dev/null +++ b/sandbox/win/src/sharedmem_ipc_client.cc @@ -0,0 +1,152 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/crosscall_params.h" +#include "base/logging.h" + +namespace sandbox { + +// Get the base of the data buffer of the channel; this is where the input +// parameters get serialized. Since they get serialized directly into the +// channel we avoid one copy. +void* SharedMemIPCClient::GetBuffer() { + bool failure = false; + size_t ix = LockFreeChannel(&failure); + if (failure) { + return NULL; + } + return reinterpret_cast(control_) + + control_->channels[ix].channel_base; +} + +// If we need to cancel an IPC before issuing DoCall +// our client should call FreeBuffer with the same pointer +// returned by GetBuffer. +void SharedMemIPCClient::FreeBuffer(void* buffer) { + size_t num = ChannelIndexFromBuffer(buffer); + ChannelControl* channel = control_->channels; + LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel); + DCHECK(kFreeChannel != result); + result; +} + +// The constructor simply casts the shared memory to the internal +// structures. This is a cheap step that is why this IPC object can +// and should be constructed per call. +SharedMemIPCClient::SharedMemIPCClient(void* shared_mem) + : control_(reinterpret_cast(shared_mem)) { + first_base_ = reinterpret_cast(shared_mem) + + control_->channels[0].channel_base; + // There must be at least one channel. + DCHECK(0 != control_->channels_count); +} + +// Do the IPC. At this point the channel should have already been +// filled with the serialized input parameters. +// We follow the pattern explained in the header file. +ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params, + CrossCallReturn* answer) { + if (!control_->server_alive) + return SBOX_ERROR_CHANNEL_ERROR; + + size_t num = ChannelIndexFromBuffer(params->GetBuffer()); + ChannelControl* channel = control_->channels; + // Note that the IPC tag goes outside the buffer as well inside + // the buffer. This should enable the server to prioritize based on + // IPC tags without having to de-serialize the entire message. + channel[num].ipc_tag = params->GetTag(); + + // Wait for the server to service this IPC call. After kIPCWaitTimeOut1 + // we check if the server_alive mutex was abandoned which will indicate + // that the server has died. + + // While the atomic signaling and waiting is not a requirement, it + // is nice because we save a trip to kernel. + DWORD wait = ::SignalObjectAndWait(channel[num].ping_event, + channel[num].pong_event, + kIPCWaitTimeOut1, FALSE); + if (WAIT_TIMEOUT == wait) { + // The server is taking too long. Enter a loop were we check if the + // server_alive mutex has been abandoned which would signal a server crash + // or else we keep waiting for a response. + while (true) { + wait = ::WaitForSingleObject(control_->server_alive, 0); + if (WAIT_TIMEOUT == wait) { + // Server seems still alive. We already signaled so here we just wait. + wait = ::WaitForSingleObject(channel[num].pong_event, kIPCWaitTimeOut1); + if (WAIT_OBJECT_0 == wait) { + // The server took a long time but responded. + break; + } else if (WAIT_TIMEOUT == wait) { + continue; + } else { + return SBOX_ERROR_CHANNEL_ERROR; + } + } else { + // The server has crashed and windows has signaled the mutex as + // abandoned. + ::InterlockedExchange(&channel[num].state, kAbandonnedChannel); + control_->server_alive = 0; + return SBOX_ERROR_CHANNEL_ERROR; + } + } + } else if (WAIT_OBJECT_0 != wait) { + // Probably the server crashed before the kIPCWaitTimeOut1 occurred. + return SBOX_ERROR_CHANNEL_ERROR; + } + + // The server has returned an answer, copy it and free the channel. + memcpy(answer, params->GetCallReturn(), sizeof(CrossCallReturn)); + + // Return the IPC state It can indicate that while the IPC has + // completed some error in the Broker has caused to not return valid + // results. + return answer->call_outcome; +} + +// Locking a channel is a simple as looping over all the channels +// looking for one that is has state = kFreeChannel and atomically +// swapping it to kBusyChannel. +// If there is no free channel, then we must back off so some other +// thread makes progress and frees a channel. To back off we sleep. +size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) { + if (0 == control_->channels_count) { + *severe_failure = true; + return 0; + } + ChannelControl* channel = control_->channels; + do { + for (size_t ix = 0; ix != control_->channels_count; ++ix) { + if (kFreeChannel == ::InterlockedCompareExchange(&channel[ix].state, + kBusyChannel, + kFreeChannel)) { + *severe_failure = false; + return ix; + } + } + // We did not find any available channel, maybe the server is dead. + DWORD wait = ::WaitForSingleObject(control_->server_alive, + kIPCWaitTimeOut2); + if (WAIT_TIMEOUT != wait) { + // The server is dead and we outlive it enough to get in trouble. + *severe_failure = true; + return 0; + } + } + while (true); +} + +// Find out which channel we are from the pointer returned by GetBuffer. +size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) { + ptrdiff_t d = reinterpret_cast(buffer) - first_base_; + size_t num = d/kIPCChannelSize; + DCHECK(num < control_->channels_count); + return (num); +} + +} // namespace sandbox diff --git a/sandbox/win/src/sharedmem_ipc_client.h b/sandbox/win/src/sharedmem_ipc_client.h new file mode 100644 index 0000000..90d0022 --- /dev/null +++ b/sandbox/win/src/sharedmem_ipc_client.h @@ -0,0 +1,136 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__ +#define SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__ + +#include "sandbox/src/crosscall_params.h" +#include "sandbox/src/sandbox.h" + +// IPC transport implementation that uses shared memory. +// This is the client side +// +// The shared memory is divided on blocks called channels, and potentially +// it can perform as many concurrent IPC calls as channels. The IPC over +// each channel is strictly synchronous for the client. +// +// Each channel as a channel control section associated with. Each control +// section has two kernel events (known as ping and pong) and a integer +// variable that maintains a state +// +// this is the state diagram of a channel: +// +// locked in service +// kFreeChannel---------->BusyChannel-------------->kAckChannel +// ^ | +// |_________________________________________________| +// answer ready +// +// The protocol is as follows: +// 1) client finds a free channel: state = kFreeChannel +// 2) does an atomic compare-and-swap, now state = BusyChannel +// 3) client writes the data into the channel buffer +// 4) client signals the ping event and waits (blocks) on the pong event +// 5) eventually the server signals the pong event +// 6) the client awakes and reads the answer from the same channel +// 7) the client updates its InOut parameters with the new data from the +// shared memory section. +// 8) the client atomically sets the state = kFreeChannel +// +// In the shared memory the layout is as follows: +// +// [ channel count ] +// [ channel control 0] +// [ channel control 1] +// [ channel control N] +// [ channel buffer 0 ] 1024 bytes +// [ channel buffer 1 ] 1024 bytes +// [ channel buffer N ] 1024 bytes +// +// By default each channel buffer is 1024 bytes +namespace sandbox { + +// the possible channel states as described above +enum ChannelState { + // channel is free + kFreeChannel = 1, + // IPC in progress client side + kBusyChannel, + // IPC in progress server side + kAckChannel, + // not used right now + kReadyChannel, + // IPC abandoned by client side + kAbandonnedChannel +}; + +// The next two constants control the time outs for the IPC. +const DWORD kIPCWaitTimeOut1 = 1000; // Milliseconds. +const DWORD kIPCWaitTimeOut2 = 50; // Milliseconds. + +// the channel control structure +struct ChannelControl { + // points to be beginning of the channel buffer, where data goes + size_t channel_base; + // maintains the state from the ChannelState enumeration + volatile LONG state; + // the ping event is signaled by the client when the IPC data is ready on + // the buffer + HANDLE ping_event; + // the client waits on the pong event for the IPC answer back + HANDLE pong_event; + // the IPC unique identifier + uint32 ipc_tag; +}; + +struct IPCControl { + // total number of channels available, some might be busy at a given time + size_t channels_count; + // handle to a shared mutex to detect when the server is dead + HANDLE server_alive; + // array of channel control structures + ChannelControl channels[1]; +}; + +// the actual shared memory IPC implementation class. This object is designed +// to be lightweight so it can be constructed on-site (at the calling place) +// wherever an IPC call is needed. +class SharedMemIPCClient { + public: + // Creates the IPC client. + // as parameter it takes the base address of the shared memory + explicit SharedMemIPCClient(void* shared_mem); + + // locks a free channel and returns the channel buffer memory base. This call + // blocks until there is a free channel + void* GetBuffer(); + + // releases the lock on the channel, for other to use. call this if you have + // called GetBuffer and you want to abort but have not called yet DoCall() + void FreeBuffer(void* buffer); + + // Performs the actual IPC call. + // params: The blob of packed input parameters. + // answer: upon IPC completion, it contains the server answer to the IPC. + // If the return value is not SBOX_ERROR_CHANNEL_ERROR, the caller has to free + // the channel. + // returns ALL_OK if the IPC mechanism successfully delivered. You still need + // to check on the answer structure to see the actual IPC result. + ResultCode DoCall(CrossCallParams* params, CrossCallReturn* answer); + + private: + // Returns the index of the first free channel. It sets 'severe_failure' + // to true if there is an unrecoverable error that does not allow to + // find a channel. + size_t LockFreeChannel(bool* severe_failure); + // Return the channel index given the address of the buffer. + size_t ChannelIndexFromBuffer(const void* buffer); + IPCControl* control_; + // point to the first channel base + char* first_base_; +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__ diff --git a/sandbox/win/src/sharedmem_ipc_server.cc b/sandbox/win/src/sharedmem_ipc_server.cc new file mode 100644 index 0000000..ba90d1b --- /dev/null +++ b/sandbox/win/src/sharedmem_ipc_server.cc @@ -0,0 +1,410 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/callback.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "sandbox/src/sharedmem_ipc_server.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/crosscall_params.h" +#include "sandbox/src/crosscall_server.h" + +namespace sandbox { + +SharedMemIPCServer::SharedMemIPCServer(HANDLE target_process, + DWORD target_process_id, + HANDLE target_job, + ThreadProvider* thread_provider, + Dispatcher* dispatcher) + : client_control_(NULL), + thread_provider_(thread_provider), + target_process_(target_process), + target_process_id_(target_process_id), + target_job_object_(target_job), + call_dispatcher_(dispatcher) { +} + +SharedMemIPCServer::~SharedMemIPCServer() { + // Free the wait handles associated with the thread pool. + if (!thread_provider_->UnRegisterWaits(this)) { + // Better to leak than to crash. + return; + } + // Free the IPC signal events. + ServerContexts::iterator it; + for (it = server_contexts_.begin(); it != server_contexts_.end(); ++it) { + ServerControl* context = (*it); + ::CloseHandle(context->ping_event); + ::CloseHandle(context->pong_event); + delete context; + } +} + +bool SharedMemIPCServer::Init(void* shared_mem, uint32 shared_size, + uint32 channel_size) { + // The shared memory needs to be at least as big as a channel. + if (shared_size < channel_size) { + return false; + } + // The channel size should be aligned. + if (0 != (channel_size % 32)) { + return false; + } + + // Calculate how many channels we can fit in the shared memory. + shared_size -= offsetof(IPCControl, channels); + size_t channel_count = shared_size / (sizeof(ChannelControl) + channel_size); + + // If we cannot fit even one channel we bail out. + if (0 == channel_count) { + return false; + } + // Calculate the start of the first channel. + size_t base_start = (sizeof(ChannelControl)* channel_count) + + offsetof(IPCControl, channels); + + client_control_ = reinterpret_cast(shared_mem); + client_control_->channels_count = 0; + + // This is the initialization that we do per-channel. Basically: + // 1) make two events (ping & pong) + // 2) create handles to the events for the client and the server. + // 3) initialize the channel (client_context) with the state. + // 4) initialize the server side of the channel (service_context). + // 5) call the thread provider RegisterWait to register the ping events. + for (size_t ix = 0; ix != channel_count; ++ix) { + ChannelControl* client_context = &client_control_->channels[ix]; + ServerControl* service_context = new ServerControl; + server_contexts_.push_back(service_context); + + if (!MakeEvents(&service_context->ping_event, + &service_context->pong_event, + &client_context->ping_event, + &client_context->pong_event)) { + return false; + } + + client_context->channel_base = base_start; + client_context->state = kFreeChannel; + + // Note that some of these values are available as members of this + // object but we put them again into the service_context because we + // will be called on a static method (ThreadPingEventReady) + service_context->shared_base = reinterpret_cast(shared_mem); + service_context->channel_size = channel_size; + service_context->channel = client_context; + service_context->channel_buffer = service_context->shared_base + + client_context->channel_base; + service_context->dispatcher = call_dispatcher_; + service_context->target_info.process = target_process_; + service_context->target_info.process_id = target_process_id_; + service_context->target_info.job_object = target_job_object_; + // Advance to the next channel. + base_start += channel_size; + // Register the ping event with the threadpool. + thread_provider_->RegisterWait(this, service_context->ping_event, + ThreadPingEventReady, service_context); + } + + // We create a mutex that the server locks. If the server dies unexpectedly, + // the thread that owns it will fail to release the lock and windows will + // report to the target (when it tries to acquire it) that the wait was + // abandoned. Note: We purposely leak the local handle because we want it to + // be closed by Windows itself so it is properly marked as abandoned if the + // server dies. + if (!::DuplicateHandle(::GetCurrentProcess(), + ::CreateMutexW(NULL, TRUE, NULL), + target_process_, &client_control_->server_alive, + SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, 0)) { + return false; + } + // This last setting indicates to the client all is setup. + client_control_->channels_count = channel_count; + return true; +} + +// Releases memory allocated for IPC arguments, if needed. +void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]) { + for (size_t i = 0; i < kMaxIpcParams; i++) { + switch (ipc_params->args[i]) { + case WCHAR_TYPE: { + delete reinterpret_cast(args[i]); + args[i] = NULL; + break; + } + case INOUTPTR_TYPE: { + delete reinterpret_cast(args[i]); + args[i] = NULL; + break; + } + default: break; + } + } +} + +// Fills up the list of arguments (args and ipc_params) for an IPC call. +bool GetArgs(CrossCallParamsEx* params, IPCParams* ipc_params, + void* args[kMaxIpcParams]) { + if (kMaxIpcParams < params->GetParamsCount()) + return false; + + for (uint32 i = 0; i < params->GetParamsCount(); i++) { + uint32 size; + ArgType type; + args[i] = params->GetRawParameter(i, &size, &type); + if (args[i]) { + ipc_params->args[i] = type; + switch (type) { + case WCHAR_TYPE: { + scoped_ptr data(new std::wstring); + if (!params->GetParameterStr(i, data.get())) { + args[i] = 0; + ReleaseArgs(ipc_params, args); + return false; + } + args[i] = data.release(); + break; + } + case ULONG_TYPE: { + uint32 data; + if (!params->GetParameter32(i, &data)) { + ReleaseArgs(ipc_params, args); + return false; + } + IPCInt ipc_int(data); + args[i] = ipc_int.AsVoidPtr(); + break; + } + case VOIDPTR_TYPE : { + void* data; + if (!params->GetParameterVoidPtr(i, &data)) { + ReleaseArgs(ipc_params, args); + return false; + } + args[i] = data; + break; + } + case INOUTPTR_TYPE: { + if (!args[i]) { + ReleaseArgs(ipc_params, args); + return false; + } + CountedBuffer* buffer = new CountedBuffer(args[i] , size); + args[i] = buffer; + break; + } + default: break; + } + } + } + return true; +} + +bool SharedMemIPCServer::InvokeCallback(const ServerControl* service_context, + void* ipc_buffer, + CrossCallReturn* call_result) { + // Set the default error code; + SetCallError(SBOX_ERROR_INVALID_IPC, call_result); + uint32 output_size = 0; + // Parse, verify and copy the message. The handler operates on a copy + // of the message so the client cannot play dirty tricks by changing the + // data in the channel while the IPC is being processed. + scoped_ptr params( + CrossCallParamsEx::CreateFromBuffer(ipc_buffer, + service_context->channel_size, + &output_size)); + if (!params.get()) + return false; + + uint32 tag = params->GetTag(); + COMPILE_ASSERT(0 == INVALID_TYPE, Incorrect_type_enum); + IPCParams ipc_params = {0}; + ipc_params.ipc_tag = tag; + + void* args[kMaxIpcParams]; + if (!GetArgs(params.get(), &ipc_params, args)) + return false; + + IPCInfo ipc_info = {0}; + ipc_info.ipc_tag = tag; + ipc_info.client_info = &service_context->target_info; + Dispatcher* dispatcher = service_context->dispatcher; + DCHECK(dispatcher); + bool error = true; + Dispatcher* handler = NULL; + + Dispatcher::CallbackGeneric callback_generic; + handler = dispatcher->OnMessageReady(&ipc_params, &callback_generic); + if (handler) { + switch (params->GetParamsCount()) { + case 0: { + // Ask the IPC dispatcher if she can service this IPC. + Dispatcher::Callback0 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info)) + break; + error = false; + break; + } + case 1: { + Dispatcher::Callback1 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info, args[0])) + break; + error = false; + break; + } + case 2: { + Dispatcher::Callback2 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info, args[0], args[1])) + break; + error = false; + break; + } + case 3: { + Dispatcher::Callback3 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2])) + break; + error = false; + break; + } + case 4: { + Dispatcher::Callback4 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], + args[3])) + break; + error = false; + break; + } + case 5: { + Dispatcher::Callback5 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], + args[4])) + break; + error = false; + break; + } + case 6: { + Dispatcher::Callback6 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], + args[4], args[5])) + break; + error = false; + break; + } + case 7: { + Dispatcher::Callback7 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], + args[4], args[5], args[6])) + break; + error = false; + break; + } + case 8: { + Dispatcher::Callback8 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], + args[4], args[5], args[6], args[7])) + break; + error = false; + break; + } + case 9: { + Dispatcher::Callback9 callback = + reinterpret_cast(callback_generic); + if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3], + args[4], args[5], args[6], args[7], args[8])) + break; + error = false; + break; + } + default: { + NOTREACHED(); + break; + } + } + } + + if (error) { + if (handler) + SetCallError(SBOX_ERROR_FAILED_IPC, call_result); + } else { + memcpy(call_result, &ipc_info.return_info, sizeof(*call_result)); + SetCallSuccess(call_result); + if (params->IsInOut()) { + // Maybe the params got changed by the broker. We need to upadte the + // memory section. + memcpy(ipc_buffer, params.get(), output_size); + } + } + + ReleaseArgs(&ipc_params, args); + + return !error; +} + +// This function gets called by a thread from the thread pool when a +// ping event fires. The context is the same as passed in the RegisterWait() +// call above. +void __stdcall SharedMemIPCServer::ThreadPingEventReady(void* context, + unsigned char) { + if (NULL == context) { + DCHECK(false); + return; + } + ServerControl* service_context = reinterpret_cast(context); + // Since the event fired, the channel *must* be busy. Change to kAckChannel + // while we service it. + LONG last_state = + ::InterlockedCompareExchange(&service_context->channel->state, + kAckChannel, kBusyChannel); + if (kBusyChannel != last_state) { + DCHECK(false); + return; + } + + // Prepare the result structure. At this point we will return some result + // even if the IPC is invalid, malformed or has no handler. + CrossCallReturn call_result = {0}; + void* buffer = service_context->channel_buffer; + + InvokeCallback(service_context, buffer, &call_result); + + // Copy the answer back into the channel and signal the pong event. This + // should wake up the client so he can finish the the ipc cycle. + CrossCallParams* call_params = reinterpret_cast(buffer); + memcpy(call_params->GetCallReturn(), &call_result, sizeof(call_result)); + ::InterlockedExchange(&service_context->channel->state, kAckChannel); + ::SetEvent(service_context->pong_event); +} + +bool SharedMemIPCServer::MakeEvents(HANDLE* server_ping, HANDLE* server_pong, + HANDLE* client_ping, HANDLE* client_pong) { + // Note that the IPC client has no right to delete the events. That would + // cause problems. The server *owns* the events. + const DWORD kDesiredAccess = SYNCHRONIZE | EVENT_MODIFY_STATE; + + // The events are auto reset, and start not signaled. + *server_ping = ::CreateEventW(NULL, FALSE, FALSE, NULL); + if (!::DuplicateHandle(::GetCurrentProcess(), *server_ping, target_process_, + client_ping, kDesiredAccess, FALSE, 0)) { + return false; + } + *server_pong = ::CreateEventW(NULL, FALSE, FALSE, NULL); + if (!::DuplicateHandle(::GetCurrentProcess(), *server_pong, target_process_, + client_pong, kDesiredAccess, FALSE, 0)) { + return false; + } + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/sharedmem_ipc_server.h b/sandbox/win/src/sharedmem_ipc_server.h new file mode 100644 index 0000000..7e10174 --- /dev/null +++ b/sandbox/win/src/sharedmem_ipc_server.h @@ -0,0 +1,127 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_ +#define SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_ + +#include + +#include "base/basictypes.h" +#include "base/gtest_prod_util.h" +#include "sandbox/src/crosscall_params.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sharedmem_ipc_client.h" + +// IPC transport implementation that uses shared memory. +// This is the server side +// +// The server side has knowledge about the layout of the shared memory +// and the state transitions. Both are explained in sharedmem_ipc_client.h +// +// As opposed to SharedMemIPClient, the Server object should be one for the +// entire lifetime of the target process. The server is in charge of creating +// the events (ping, pong) both for the client and for the target that are used +// to signal the IPC and also in charge of setting the initial state of the +// channels. +// +// When an IPC is ready, the server relies on being called by on the +// ThreadPingEventReady callback. The IPC server then retrieves the buffer, +// marshals it into a CrossCallParam object and calls the Dispatcher, who is in +// charge of fulfilling the IPC request. +namespace sandbox { + +// the shared memory implementation of the IPC server. There should be one +// of these objects per target (IPC client) process +class SharedMemIPCServer { + public: + // Creates the IPC server. + // target_process: handle to the target process. It must be suspended. + // target_process_id: process id of the target process. + // target_job: the job object handle associated with the target process. + // thread_provider: a thread provider object. + // dispatcher: an object that can service IPC calls. + SharedMemIPCServer(HANDLE target_process, DWORD target_process_id, + HANDLE target_job, ThreadProvider* thread_provider, + Dispatcher* dispatcher); + + ~SharedMemIPCServer(); + + // Initializes the server structures, shared memory structures and + // creates the kernels events used to signal the IPC. + bool Init(void* shared_mem, uint32 shared_size, uint32 channel_size); + + private: + // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes + // do not work with sandbox tests. + FRIEND_TEST_ALL_PREFIXES(IPCTest, SharedMemServerTests); + // When an event fires (IPC request). A thread from the ThreadProvider + // will call this function. The context parameter should be the same as + // provided when ThreadProvider::RegisterWait was called. + static void __stdcall ThreadPingEventReady(void* context, + unsigned char); + + // Makes the client and server events. This function is called once + // per channel. + bool MakeEvents(HANDLE* server_ping, HANDLE* server_pong, + HANDLE* client_ping, HANDLE* client_pong); + + // A copy this structure is maintained per channel. + // Note that a lot of the fields are just the same of what we have in the IPC + // object itself. It is better to have the copies since we can dispatch in the + // static method without worrying about converting back to a member function + // call or about threading issues. + struct ServerControl { + // This channel server ping event. + HANDLE ping_event; + // This channel server pong event. + HANDLE pong_event; + // The size of this channel. + uint32 channel_size; + // The pointer to the actual channel data. + char* channel_buffer; + // The pointer to the base of the shared memory. + char* shared_base; + // A pointer to this channel's client-side control structure this structure + // lives in the shared memory. + ChannelControl* channel; + // the IPC dispatcher associated with this channel. + Dispatcher* dispatcher; + // The target process information associated with this channel. + ClientInfo target_info; + }; + + // Looks for the appropriate handler for this IPC and invokes it. + static bool InvokeCallback(const ServerControl* service_context, + void* ipc_buffer, CrossCallReturn* call_result); + + // Points to the shared memory channel control which lives at + // the start of the shared section. + IPCControl* client_control_; + + // Keeps track of the server side objects that are used to answer an IPC. + typedef std::list ServerContexts; + ServerContexts server_contexts_; + + // The thread provider provides the threads that call back into this object + // when the IPC events fire. + ThreadProvider* thread_provider_; + + // The IPC object is associated with a target process. + HANDLE target_process_; + + // The target process id associated with the IPC object. + DWORD target_process_id_; + + // The target object is inside a job too. + HANDLE target_job_object_; + + // The dispatcher handles 'ready' IPC calls. + Dispatcher* call_dispatcher_; + + DISALLOW_COPY_AND_ASSIGN(SharedMemIPCServer); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_ diff --git a/sandbox/win/src/sid.cc b/sandbox/win/src/sid.cc new file mode 100644 index 0000000..6ed9963 --- /dev/null +++ b/sandbox/win/src/sid.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/sid.h" + +#include "base/logging.h" + +namespace sandbox { + +Sid::Sid(const SID *sid) { + ::CopySid(SECURITY_MAX_SID_SIZE, sid_, const_cast(sid)); +}; + +Sid::Sid(WELL_KNOWN_SID_TYPE type) { + DWORD size_sid = SECURITY_MAX_SID_SIZE; + BOOL result = ::CreateWellKnownSid(type, NULL, sid_, &size_sid); + DCHECK(result); + DBG_UNREFERENCED_LOCAL_VARIABLE(result); +} + +const SID *Sid::GetPSID() const { + return reinterpret_cast(const_cast(sid_)); +} + +} // namespace sandbox diff --git a/sandbox/win/src/sid.h b/sandbox/win/src/sid.h new file mode 100644 index 0000000..4656859 --- /dev/null +++ b/sandbox/win/src/sid.h @@ -0,0 +1,29 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SID_H_ +#define SANDBOX_SRC_SID_H_ + +#include + +namespace sandbox { + +// This class is used to hold and generate SIDS. +class Sid { + public: + // Constructors initializing the object with the SID passed. + // This is a converting constructor. It is not explicit. + Sid(const SID *sid); + Sid(WELL_KNOWN_SID_TYPE type); + + // Returns sid_. + const SID *GetPSID() const; + + private: + BYTE sid_[SECURITY_MAX_SID_SIZE]; +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SID_H_ diff --git a/sandbox/win/src/sid_unittest.cc b/sandbox/win/src/sid_unittest.cc new file mode 100644 index 0000000..f1ac60a --- /dev/null +++ b/sandbox/win/src/sid_unittest.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains unit tests for the sid class. + +#define _ATL_NO_EXCEPTIONS +#include +#include + +#include "sandbox/src/sid.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +// Calls ::EqualSid. This function exists only to simplify the calls to +// ::EqualSid by removing the need to cast the input params. +BOOL EqualSid(const SID *sid1, const SID *sid2) { + return ::EqualSid(const_cast(sid1), const_cast(sid2)); +} + +// Tests the creation if a Sid +TEST(SidTest, Constructors) { + ATL::CSid sid_world = ATL::Sids::World(); + SID *sid_world_pointer = const_cast(sid_world.GetPSID()); + + // Check the SID* constructor + Sid sid_sid_star(sid_world_pointer); + ASSERT_TRUE(EqualSid(sid_world_pointer, sid_sid_star.GetPSID())); + + // Check the copy constructor + Sid sid_copy(sid_sid_star); + ASSERT_TRUE(EqualSid(sid_world_pointer, sid_copy.GetPSID())); + + // Note that the WELL_KNOWN_SID_TYPE constructor is tested in the GetPSID + // test. +} + +// Tests the method GetPSID +TEST(SidTest, GetPSID) { + // Check for non-null result; + ASSERT_NE(static_cast(NULL), Sid(::WinLocalSid).GetPSID()); + ASSERT_NE(static_cast(NULL), Sid(::WinCreatorOwnerSid).GetPSID()); + ASSERT_NE(static_cast(NULL), Sid(::WinBatchSid).GetPSID()); + + ASSERT_TRUE(EqualSid(Sid(::WinNullSid).GetPSID(), + ATL::Sids::Null().GetPSID())); + + ASSERT_TRUE(EqualSid(Sid(::WinWorldSid).GetPSID(), + ATL::Sids::World().GetPSID())); + + ASSERT_TRUE(EqualSid(Sid(::WinDialupSid).GetPSID(), + ATL::Sids::Dialup().GetPSID())); + + ASSERT_TRUE(EqualSid(Sid(::WinNetworkSid).GetPSID(), + ATL::Sids::Network().GetPSID())); + + ASSERT_TRUE(EqualSid(Sid(::WinBuiltinAdministratorsSid).GetPSID(), + ATL::Sids::Admins().GetPSID())); + + ASSERT_TRUE(EqualSid(Sid(::WinBuiltinUsersSid).GetPSID(), + ATL::Sids::Users().GetPSID())); + + ASSERT_TRUE(EqualSid(Sid(::WinBuiltinGuestsSid).GetPSID(), + ATL::Sids::Guests().GetPSID())); + + ASSERT_TRUE(EqualSid(Sid(::WinProxySid).GetPSID(), + ATL::Sids::Proxy().GetPSID())); +} + +} // namespace sandbox diff --git a/sandbox/win/src/sidestep/ia32_modrm_map.cpp b/sandbox/win/src/sidestep/ia32_modrm_map.cpp new file mode 100644 index 0000000..b22ab1d --- /dev/null +++ b/sandbox/win/src/sidestep/ia32_modrm_map.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Table of relevant information about how to decode the ModR/M byte. +// Based on information in the IA-32 Intel Architecture +// Software Developer's Manual Volume 2: Instruction Set Reference. + +#include "sandbox/src/sidestep/mini_disassembler.h" +#include "sandbox/src/sidestep/mini_disassembler_types.h" + +namespace sidestep { + +const ModrmEntry MiniDisassembler::s_ia16_modrm_map_[] = { +// mod == 00 + /* r/m == 000 */ { false, false, OS_ZERO }, + /* r/m == 001 */ { false, false, OS_ZERO }, + /* r/m == 010 */ { false, false, OS_ZERO }, + /* r/m == 011 */ { false, false, OS_ZERO }, + /* r/m == 100 */ { false, false, OS_ZERO }, + /* r/m == 101 */ { false, false, OS_ZERO }, + /* r/m == 110 */ { true, false, OS_WORD }, + /* r/m == 111 */ { false, false, OS_ZERO }, +// mod == 01 + /* r/m == 000 */ { true, false, OS_BYTE }, + /* r/m == 001 */ { true, false, OS_BYTE }, + /* r/m == 010 */ { true, false, OS_BYTE }, + /* r/m == 011 */ { true, false, OS_BYTE }, + /* r/m == 100 */ { true, false, OS_BYTE }, + /* r/m == 101 */ { true, false, OS_BYTE }, + /* r/m == 110 */ { true, false, OS_BYTE }, + /* r/m == 111 */ { true, false, OS_BYTE }, +// mod == 10 + /* r/m == 000 */ { true, false, OS_WORD }, + /* r/m == 001 */ { true, false, OS_WORD }, + /* r/m == 010 */ { true, false, OS_WORD }, + /* r/m == 011 */ { true, false, OS_WORD }, + /* r/m == 100 */ { true, false, OS_WORD }, + /* r/m == 101 */ { true, false, OS_WORD }, + /* r/m == 110 */ { true, false, OS_WORD }, + /* r/m == 111 */ { true, false, OS_WORD }, +// mod == 11 + /* r/m == 000 */ { false, false, OS_ZERO }, + /* r/m == 001 */ { false, false, OS_ZERO }, + /* r/m == 010 */ { false, false, OS_ZERO }, + /* r/m == 011 */ { false, false, OS_ZERO }, + /* r/m == 100 */ { false, false, OS_ZERO }, + /* r/m == 101 */ { false, false, OS_ZERO }, + /* r/m == 110 */ { false, false, OS_ZERO }, + /* r/m == 111 */ { false, false, OS_ZERO } +}; + +const ModrmEntry MiniDisassembler::s_ia32_modrm_map_[] = { +// mod == 00 + /* r/m == 000 */ { false, false, OS_ZERO }, + /* r/m == 001 */ { false, false, OS_ZERO }, + /* r/m == 010 */ { false, false, OS_ZERO }, + /* r/m == 011 */ { false, false, OS_ZERO }, + /* r/m == 100 */ { false, true, OS_ZERO }, + /* r/m == 101 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 110 */ { false, false, OS_ZERO }, + /* r/m == 111 */ { false, false, OS_ZERO }, +// mod == 01 + /* r/m == 000 */ { true, false, OS_BYTE }, + /* r/m == 001 */ { true, false, OS_BYTE }, + /* r/m == 010 */ { true, false, OS_BYTE }, + /* r/m == 011 */ { true, false, OS_BYTE }, + /* r/m == 100 */ { true, true, OS_BYTE }, + /* r/m == 101 */ { true, false, OS_BYTE }, + /* r/m == 110 */ { true, false, OS_BYTE }, + /* r/m == 111 */ { true, false, OS_BYTE }, +// mod == 10 + /* r/m == 000 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 001 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 010 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 011 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 100 */ { true, true, OS_DOUBLE_WORD }, + /* r/m == 101 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 110 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 111 */ { true, false, OS_DOUBLE_WORD }, +// mod == 11 + /* r/m == 000 */ { false, false, OS_ZERO }, + /* r/m == 001 */ { false, false, OS_ZERO }, + /* r/m == 010 */ { false, false, OS_ZERO }, + /* r/m == 011 */ { false, false, OS_ZERO }, + /* r/m == 100 */ { false, false, OS_ZERO }, + /* r/m == 101 */ { false, false, OS_ZERO }, + /* r/m == 110 */ { false, false, OS_ZERO }, + /* r/m == 111 */ { false, false, OS_ZERO }, +}; + +}; // namespace sidestep diff --git a/sandbox/win/src/sidestep/ia32_opcode_map.cpp b/sandbox/win/src/sidestep/ia32_opcode_map.cpp new file mode 100644 index 0000000..9f37cef --- /dev/null +++ b/sandbox/win/src/sidestep/ia32_opcode_map.cpp @@ -0,0 +1,1159 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Opcode decoding maps. Based on the IA-32 Intel Architecture +// Software Developer's Manual Volume 2: Instruction Set Reference. Idea +// for how to lay out the tables in memory taken from the implementation +// in the Bastard disassembly environment. + +#include "sandbox/src/sidestep/mini_disassembler.h" + +namespace sidestep { + +/* +* This is the first table to be searched; the first field of each +* Opcode in the table is either 0 to indicate you're in the +* right table, or an index to the correct table, in the global +* map g_pentiumOpcodeMap +*/ +const Opcode s_first_opcode_byte[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF */ { 1, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x10 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x11 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x12 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x13 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x14 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x15 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x16 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x17 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x18 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x19 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1E */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1F */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x20 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x21 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x22 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x23 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x24 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x25 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x26 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x27 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "daa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x28 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x29 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "das", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x30 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x31 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x32 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x33 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x34 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x35 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x36 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x37 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aaa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x38 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x39 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aas", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x40 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x41 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x42 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x43 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x44 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x45 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x46 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x47 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x48 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x49 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x50 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x51 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x52 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x53 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x54 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x55 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x56 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x57 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x58 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x59 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x60 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x61 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x62 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_A, AM_NOT_USED, "bound", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x63 */ { 0, IT_GENERIC, AM_E | OT_W, AM_G | OT_W, AM_NOT_USED, "arpl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x64 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x65 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x66 */ { 0, IT_PREFIX_OPERAND, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x67 */ { 0, IT_PREFIX_ADDRESS, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x68 */ { 0, IT_GENERIC, AM_I | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x69 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_V, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6A */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_B, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6C */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "insb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6D */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "insd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6E */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X | OT_B, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X | OT_V, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x70 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x71 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x72 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x73 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x74 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x75 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x76 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x77 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x78 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x79 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7A */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7B */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7C */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7D */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7E */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7F */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x80 */ { 2, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x81 */ { 3, IT_REFERENCE, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x82 */ { 4, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x83 */ { 5, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x84 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x85 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x86 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x87 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x88 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x89 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8C */ { 0, IT_GENERIC, AM_E | OT_W, AM_S | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8D */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, "lea", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8E */ { 0, IT_GENERIC, AM_S | OT_W, AM_E | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8F */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x90 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "nop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x91 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x92 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x93 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x94 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x95 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x96 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x97 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x98 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cwde", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x99 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cdq", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9A */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "callf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9B */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wait", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9E */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_O | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_O | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA2 */ { 0, IT_GENERIC, AM_O | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA3 */ { 0, IT_GENERIC, AM_O | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA4 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "movsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA5 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "movsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA6 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "cmpsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA7 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "cmpsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAA */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "stosb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAB */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "stosd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X| OT_B, AM_NOT_USED, "lodsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X| OT_V, AM_NOT_USED, "lodsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAE */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_Y | OT_B, AM_NOT_USED, "scasb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_Y | OT_V, AM_NOT_USED, "scasd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB1 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB2 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB3 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB8 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBA */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBB */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBC */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBE */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC0 */ { 6, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC1 */ { 7, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC2 */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC3 */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC4 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "les", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "lds", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC8 */ { 0, IT_GENERIC, AM_I | OT_W, AM_I | OT_B, AM_NOT_USED, "enter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "leave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCA */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCB */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "int3", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCD */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "int", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCE */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "into", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCF */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "iret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD0 */ { 8, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD1 */ { 9, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD2 */ { 10, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD3 */ { 11, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD4 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aam", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD5 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "xlat", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + + // The following 8 lines would be references to the FPU tables, but we currently + // do not support the FPU instructions in this disassembler. + + /* 0xD8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDA */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDB */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDC */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDD */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDE */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDF */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + + + /* 0xE0 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE1 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE2 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE3 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jcxz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE6 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE7 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE8 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE9 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEA */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEB */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xED */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEF */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_V, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF0 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lock:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF2 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "repne:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF3 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rep:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF4 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "hlt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF6 */ { 12, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF7 */ { 13, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cli", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFB */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFD */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "std", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFE */ { 14, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFF */ { 15, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f[] = { + /* 0x0 */ { 16, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 17, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lsl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "invd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wbinvd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud2", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x10 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movups", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "movsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "movss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movupd" } }, + /* 0x11 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movups", true, + /* F2h */ { 0, IT_GENERIC, AM_W | OT_SD, AM_V | OT_SD, AM_NOT_USED, "movsd" }, + /* F3h */ { 0, IT_GENERIC, AM_W | OT_SS, AM_V | OT_SS, AM_NOT_USED, "movss" }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movupd" } }, + /* 0x12 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // only one of ... + /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // ...these two is correct, Intel doesn't specify which + /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_S, AM_NOT_USED, "movlpd" } }, + /* 0x13 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlpd" } }, + /* 0x14 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpcklps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpcklpd" } }, + /* 0x15 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpckhps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpckhpd" } }, + /* 0x16 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // only one of... + /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // ...these two is correct, Intel doesn't specify which + /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhpd" } }, + /* 0x17 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhpd" } }, + /* 0x18 */ { 18, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x19 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1C */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x20 */ { 0, IT_GENERIC, AM_R | OT_D, AM_C | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x21 */ { 0, IT_GENERIC, AM_R | OT_D, AM_D | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x22 */ { 0, IT_GENERIC, AM_C | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x23 */ { 0, IT_GENERIC, AM_D | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x24 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x25 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x26 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x27 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x28 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movaps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movapd" } }, + /* 0x29 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movaps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movapd" } }, + /* 0x2A */ { 0, IT_GENERIC, AM_V | OT_PS, AM_Q | OT_Q, AM_NOT_USED, "cvtpi2ps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_E | OT_D, AM_NOT_USED, "cvtsi2sd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_E | OT_D, AM_NOT_USED, "cvtsi2ss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_Q | OT_DQ, AM_NOT_USED, "cvtpi2pd" } }, + /* 0x2B */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movntps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movntpd" } }, + /* 0x2C */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvttps2pi", true, + /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvttsd2si" }, + /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvttss2si" }, + /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2pi" } }, + /* 0x2D */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvtps2pi", true, + /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvtsd2si" }, + /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvtss2si" }, + /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2pi" } }, + /* 0x2E */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "ucomiss", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "ucomisd" } }, + /* 0x2F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_SS, AM_NOT_USED, "comiss", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "comisd" } }, + /* 0x30 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wrmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x31 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdtsc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x32 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x33 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdpmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x34 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysenter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x35 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysexit", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x36 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x37 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x38 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x39 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x40 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x41 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x42 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x43 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x44 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x45 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x46 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x47 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmova", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x48 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x49 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4A */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4D */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4E */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4F */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x50 */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PS, AM_NOT_USED, "movmskps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PD, AM_NOT_USED, "movmskpd" } }, + /* 0x51 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "sqrtps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "sqrtsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "sqrtss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "sqrtpd" } }, + /* 0x52 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rsqrtps", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rsqrtss" }, + /* 66h */ { 0 } }, + /* 0x53 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rcpps", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rcpss" }, + /* 66h */ { 0 } }, + /* 0x54 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andpd" } }, + /* 0x55 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andnps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andnpd" } }, + /* 0x56 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "orps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "orpd" } }, + /* 0x57 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "xorps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "xorpd" } }, + /* 0x58 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "addps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "addsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "addss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "addpd" } }, + /* 0x59 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "mulps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "mulsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "mulss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "mulpd" } }, + /* 0x5A */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PS, AM_NOT_USED, "cvtps2pd", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "cvtsd2ss" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "cvtss2sd" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PD, AM_NOT_USED, "cvtpd2ps" } }, + /* 0x5B */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2ps", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvttps2dq" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvtps2dq" } }, + /* 0x5C */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "subps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "subsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "subss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "subpd" } }, + /* 0x5D */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "minps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "minsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "minss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "minpd" } }, + /* 0x5E */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "divps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "divsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "divss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "divpd" } }, + /* 0x5F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "maxps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "maxsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "maxss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "maxpd" } }, + /* 0x60 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklbw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklbw" } }, + /* 0x61 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklwd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklwd" } }, + /* 0x62 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckldq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpckldq" } }, + /* 0x63 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packsswb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packsswb" } }, + /* 0x64 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtb" } }, + /* 0x65 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtw" } }, + /* 0x66 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtd" } }, + /* 0x67 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packuswb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packuswb" } }, + /* 0x68 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhbw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhbw" } }, + /* 0x69 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhwd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhwd" } }, + /* 0x6A */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhdq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhdq" } }, + /* 0x6B */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packssdw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "packssdw" } }, + /* 0x6C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } }, + /* 0x6D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } }, + /* 0x6E */ { 0, IT_GENERIC, AM_P | OT_D, AM_E | OT_D, AM_NOT_USED, "movd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_NOT_USED, "movd" } }, + /* 0x6F */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "movq", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqu" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqa" } }, + /* 0x70 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_I | OT_B, "pshuf", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshuflw" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufhw" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufd" } }, + /* 0x71 */ { 19, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x72 */ { 20, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x73 */ { 21, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x74 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqb" } }, + /* 0x75 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqw" } }, + /* 0x76 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqd" } }, + /* 0x77 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "emms", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + + // The following six opcodes are escapes into the MMX stuff, which this disassembler does not support. + /* 0x78 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x79 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7A */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7B */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7C */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7D */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + + /* 0x7E */ { 0, IT_GENERIC, AM_E | OT_D, AM_P | OT_D, AM_NOT_USED, "movd", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movq" }, + /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_DQ, AM_NOT_USED, "movd" } }, + /* 0x7F */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_P | OT_Q, AM_NOT_USED, "movq", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqu" }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqa" } }, + /* 0x80 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x81 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x82 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x83 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x84 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x85 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x86 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x87 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x88 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x89 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8A */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8B */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8C */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8D */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8E */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8F */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x90 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seto", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x91 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x92 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x93 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x94 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x95 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x96 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x97 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seta", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x98 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "sets", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x99 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9A */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9B */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9C */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9D */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9E */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9F */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cpuid", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA6 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA7 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rsm", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAC */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAD */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAE */ { 22, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB2 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lss", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB4 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lfs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB5 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lgs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB6 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB7 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud1", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBA */ { 23, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBC */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBD */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBE */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC2 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "cmpps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_I | OT_B, "cmpsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_I | OT_B, "cmpss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "cmppd" } }, + /* 0xC3 */ { 0, IT_GENERIC, AM_E | OT_D, AM_G | OT_D, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_E | OT_D, AM_I | OT_B, "pinsrw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_I | OT_B, "pinsrw" } }, + /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_I | OT_B, "pextrw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_I | OT_B, "pextrw" } }, + /* 0xC6 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "shufps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "shufpd" } }, + /* 0xC7 */ { 24, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC8 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC9 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCA */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCB */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCC */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCD */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCE */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCF */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlw" } }, + /* 0xD2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrld", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrld" } }, + /* 0xD3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlq" } }, + /* 0xD4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddq" } }, + /* 0xD5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmullw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmullw" } }, + /* 0xD6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "unused without prefix", true, + /* F2h */ { 0, IT_GENERIC, AM_P | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movdq2q" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_Q | OT_Q, AM_NOT_USED, "movq2dq" }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movq" } }, + /* 0xD7 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_NOT_USED, "pmovmskb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_NOT_USED, "pmovmskb" } }, + /* 0xD8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusb" } }, + /* 0xD9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusw" } }, + /* 0xDA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminub", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminub" } }, + /* 0xDB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pand", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pand" } }, + /* 0xDC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusb" } }, + /* 0xDD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusw" } }, + /* 0xDE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxub", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxub" } }, + /* 0xDF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pandn", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pandn" } }, + /* 0xE0 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgb" } }, + /* 0xE1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psraw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrqw" } }, + /* 0xE2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrad", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrad" } }, + /* 0xE3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgw" } }, + /* 0xE4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhuw" } }, + /* 0xE5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhw" } }, + /* 0xE6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2dq" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2pd" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2dq" } }, + /* 0xE7 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movntq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movntdq" } }, + /* 0xE8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsb" } }, + /* 0xE9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsw" } }, + /* 0xEA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminsw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminsw" } }, + /* 0xEB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "por", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "por" } }, + /* 0xEC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsb" } }, + /* 0xED */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsw" } }, + /* 0xEE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxsw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxsw" } }, + /* 0xEF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pxor", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pxor" } }, + /* 0xF0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllw" } }, + /* 0xF2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pslld", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pslld" } }, + /* 0xF3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllq" } }, + /* 0xF4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmuludq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmuludq" } }, + /* 0xF5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaddwd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaddwd" } }, + /* 0xF6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psadbw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psadbw" } }, + /* 0xF7 */ { 0, IT_GENERIC, AM_P | OT_PI, AM_Q | OT_PI, AM_NOT_USED, "maskmovq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "maskmovdqu" } }, + /* 0xF8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubb" } }, + /* 0xF9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubw" } }, + /* 0xFA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubd" } }, + /* 0xFB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubq" } }, + /* 0xFC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddb" } }, + /* 0xFD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddw" } }, + /* 0xFE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddd" } }, + /* 0xFF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f00[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "sldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "str", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "ltr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f01[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "smsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lmsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_M | OT_B, AM_NOT_USED, AM_NOT_USED, "invlpg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f18[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f71[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlw" } }, + /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psraw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psraw" } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllw" } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f72[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrld", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrld" } }, + /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrad", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrad" } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "pslld", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslld" } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f73[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlq" } }, + /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllq" } }, + /* 0x7 */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq" } }, +}; + +const Opcode s_opcode_byte_after_0fae[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxsave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxrstor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ldmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "mfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clflush/sfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, +}; + +const Opcode s_opcode_byte_after_0fba[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0fc7[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_Q, AM_NOT_USED, AM_NOT_USED, "cmpxch8b", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_80[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_81[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_82[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_83[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_c0[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_c1[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_d0[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_d1[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_d2[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_d3[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_f6[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_f7[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_fe[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_ff[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +/* +* A table of all the other tables, containing some extra information, e.g. +* how to mask out the byte we're looking at. +*/ +const OpcodeTable MiniDisassembler::s_ia32_opcode_map_[]={ + // One-byte opcodes and jumps to larger + /* 0 */ {s_first_opcode_byte, 0, 0xff, 0, 0xff}, + // Two-byte opcodes (second byte) + /* 1 */ {s_opcode_byte_after_0f, 0, 0xff, 0, 0xff}, + // Start of tables for opcodes using ModR/M bits as extension + /* 2 */ {s_opcode_byte_after_80, 3, 0x07, 0, 0x07}, + /* 3 */ {s_opcode_byte_after_81, 3, 0x07, 0, 0x07}, + /* 4 */ {s_opcode_byte_after_82, 3, 0x07, 0, 0x07}, + /* 5 */ {s_opcode_byte_after_83, 3, 0x07, 0, 0x07}, + /* 6 */ {s_opcode_byte_after_c0, 3, 0x07, 0, 0x07}, + /* 7 */ {s_opcode_byte_after_c1, 3, 0x07, 0, 0x07}, + /* 8 */ {s_opcode_byte_after_d0, 3, 0x07, 0, 0x07}, + /* 9 */ {s_opcode_byte_after_d1, 3, 0x07, 0, 0x07}, + /* 10 */ {s_opcode_byte_after_d2, 3, 0x07, 0, 0x07}, + /* 11 */ {s_opcode_byte_after_d3, 3, 0x07, 0, 0x07}, + /* 12 */ {s_opcode_byte_after_f6, 3, 0x07, 0, 0x07}, + /* 13 */ {s_opcode_byte_after_f7, 3, 0x07, 0, 0x07}, + /* 14 */ {s_opcode_byte_after_fe, 3, 0x07, 0, 0x01}, + /* 15 */ {s_opcode_byte_after_ff, 3, 0x07, 0, 0x07}, + /* 16 */ {s_opcode_byte_after_0f00, 3, 0x07, 0, 0x07}, + /* 17 */ {s_opcode_byte_after_0f01, 3, 0x07, 0, 0x07}, + /* 18 */ {s_opcode_byte_after_0f18, 3, 0x07, 0, 0x07}, + /* 19 */ {s_opcode_byte_after_0f71, 3, 0x07, 0, 0x07}, + /* 20 */ {s_opcode_byte_after_0f72, 3, 0x07, 0, 0x07}, + /* 21 */ {s_opcode_byte_after_0f73, 3, 0x07, 0, 0x07}, + /* 22 */ {s_opcode_byte_after_0fae, 3, 0x07, 0, 0x07}, + /* 23 */ {s_opcode_byte_after_0fba, 3, 0x07, 0, 0x07}, + /* 24 */ {s_opcode_byte_after_0fc7, 3, 0x07, 0, 0x01} +}; + +}; // namespace sidestep diff --git a/sandbox/win/src/sidestep/mini_disassembler.cpp b/sandbox/win/src/sidestep/mini_disassembler.cpp new file mode 100644 index 0000000..514522a --- /dev/null +++ b/sandbox/win/src/sidestep/mini_disassembler.cpp @@ -0,0 +1,395 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Implementation of MiniDisassembler. + +#ifdef _WIN64 +#error The code in this file should not be used on 64-bit Windows. +#endif + +#include "sandbox/src/sidestep/mini_disassembler.h" + +namespace sidestep { + +MiniDisassembler::MiniDisassembler(bool operand_default_is_32_bits, + bool address_default_is_32_bits) + : operand_default_is_32_bits_(operand_default_is_32_bits), + address_default_is_32_bits_(address_default_is_32_bits) { + Initialize(); +} + +MiniDisassembler::MiniDisassembler() + : operand_default_is_32_bits_(true), + address_default_is_32_bits_(true) { + Initialize(); +} + +InstructionType MiniDisassembler::Disassemble( + unsigned char* start_byte, + unsigned int* instruction_bytes) { + // Clean up any state from previous invocations. + Initialize(); + + // Start by processing any prefixes. + unsigned char* current_byte = start_byte; + unsigned int size = 0; + InstructionType instruction_type = ProcessPrefixes(current_byte, &size); + + if (IT_UNKNOWN == instruction_type) + return instruction_type; + + current_byte += size; + size = 0; + + // Invariant: We have stripped all prefixes, and the operand_is_32_bits_ + // and address_is_32_bits_ flags are correctly set. + + instruction_type = ProcessOpcode(current_byte, 0, &size); + + // Check for error processing instruction + if ((IT_UNKNOWN == instruction_type_) || (IT_UNUSED == instruction_type_)) { + return IT_UNKNOWN; + } + + current_byte += size; + + // Invariant: operand_bytes_ indicates the total size of operands + // specified by the opcode and/or ModR/M byte and/or SIB byte. + // pCurrentByte points to the first byte after the ModR/M byte, or after + // the SIB byte if it is present (i.e. the first byte of any operands + // encoded in the instruction). + + // We get the total length of any prefixes, the opcode, and the ModR/M and + // SIB bytes if present, by taking the difference of the original starting + // address and the current byte (which points to the first byte of the + // operands if present, or to the first byte of the next instruction if + // they are not). Adding the count of bytes in the operands encoded in + // the instruction gives us the full length of the instruction in bytes. + *instruction_bytes += operand_bytes_ + (current_byte - start_byte); + + // Return the instruction type, which was set by ProcessOpcode(). + return instruction_type_; +} + +void MiniDisassembler::Initialize() { + operand_is_32_bits_ = operand_default_is_32_bits_; + address_is_32_bits_ = address_default_is_32_bits_; + operand_bytes_ = 0; + have_modrm_ = false; + should_decode_modrm_ = false; + instruction_type_ = IT_UNKNOWN; + got_f2_prefix_ = false; + got_f3_prefix_ = false; + got_66_prefix_ = false; +} + +InstructionType MiniDisassembler::ProcessPrefixes(unsigned char* start_byte, + unsigned int* size) { + InstructionType instruction_type = IT_GENERIC; + const Opcode& opcode = s_ia32_opcode_map_[0].table_[*start_byte]; + + switch (opcode.type_) { + case IT_PREFIX_ADDRESS: + address_is_32_bits_ = !address_default_is_32_bits_; + goto nochangeoperand; + case IT_PREFIX_OPERAND: + operand_is_32_bits_ = !operand_default_is_32_bits_; + nochangeoperand: + case IT_PREFIX: + + if (0xF2 == (*start_byte)) + got_f2_prefix_ = true; + else if (0xF3 == (*start_byte)) + got_f3_prefix_ = true; + else if (0x66 == (*start_byte)) + got_66_prefix_ = true; + + instruction_type = opcode.type_; + (*size)++; + // we got a prefix, so add one and check next byte + ProcessPrefixes(start_byte + 1, size); + default: + break; // not a prefix byte + } + + return instruction_type; +} + +InstructionType MiniDisassembler::ProcessOpcode(unsigned char* start_byte, + unsigned int table_index, + unsigned int* size) { + const OpcodeTable& table = s_ia32_opcode_map_[table_index]; // Get our table + unsigned char current_byte = (*start_byte) >> table.shift_; + current_byte = current_byte & table.mask_; // Mask out the bits we will use + + // Check whether the byte we have is inside the table we have. + if (current_byte < table.min_lim_ || current_byte > table.max_lim_) { + instruction_type_ = IT_UNKNOWN; + return instruction_type_; + } + + const Opcode& opcode = table.table_[current_byte]; + if (IT_UNUSED == opcode.type_) { + // This instruction is not used by the IA-32 ISA, so we indicate + // this to the user. Probably means that we were pointed to + // a byte in memory that was not the start of an instruction. + instruction_type_ = IT_UNUSED; + return instruction_type_; + } else if (IT_REFERENCE == opcode.type_) { + // We are looking at an opcode that has more bytes (or is continued + // in the ModR/M byte). Recursively find the opcode definition in + // the table for the opcode's next byte. + (*size)++; + ProcessOpcode(start_byte + 1, opcode.table_index_, size); + return instruction_type_; + } + + const SpecificOpcode* specific_opcode = reinterpret_cast< + const SpecificOpcode*>(&opcode); + if (opcode.is_prefix_dependent_) { + if (got_f2_prefix_ && opcode.opcode_if_f2_prefix_.mnemonic_ != 0) { + specific_opcode = &opcode.opcode_if_f2_prefix_; + } else if (got_f3_prefix_ && opcode.opcode_if_f3_prefix_.mnemonic_ != 0) { + specific_opcode = &opcode.opcode_if_f3_prefix_; + } else if (got_66_prefix_ && opcode.opcode_if_66_prefix_.mnemonic_ != 0) { + specific_opcode = &opcode.opcode_if_66_prefix_; + } + } + + // Inv: The opcode type is known. + instruction_type_ = specific_opcode->type_; + + // Let's process the operand types to see if we have any immediate + // operands, and/or a ModR/M byte. + + ProcessOperand(specific_opcode->flag_dest_); + ProcessOperand(specific_opcode->flag_source_); + ProcessOperand(specific_opcode->flag_aux_); + + // Inv: We have processed the opcode and incremented operand_bytes_ + // by the number of bytes of any operands specified by the opcode + // that are stored in the instruction (not registers etc.). Now + // we need to return the total number of bytes for the opcode and + // for the ModR/M or SIB bytes if they are present. + + if (table.mask_ != 0xff) { + if (have_modrm_) { + // we're looking at a ModR/M byte so we're not going to + // count that into the opcode size + ProcessModrm(start_byte, size); + return IT_GENERIC; + } else { + // need to count the ModR/M byte even if it's just being + // used for opcode extension + (*size)++; + return IT_GENERIC; + } + } else { + if (have_modrm_) { + // The ModR/M byte is the next byte. + (*size)++; + ProcessModrm(start_byte + 1, size); + return IT_GENERIC; + } else { + (*size)++; + return IT_GENERIC; + } + } +} + +bool MiniDisassembler::ProcessOperand(int flag_operand) { + bool succeeded = true; + if (AM_NOT_USED == flag_operand) + return succeeded; + + // Decide what to do based on the addressing mode. + switch (flag_operand & AM_MASK) { + // No ModR/M byte indicated by these addressing modes, and no + // additional (e.g. immediate) parameters. + case AM_A: // Direct address + case AM_F: // EFLAGS register + case AM_X: // Memory addressed by the DS:SI register pair + case AM_Y: // Memory addressed by the ES:DI register pair + case AM_IMPLICIT: // Parameter is implicit, occupies no space in + // instruction + break; + + // There is a ModR/M byte but it does not necessarily need + // to be decoded. + case AM_C: // reg field of ModR/M selects a control register + case AM_D: // reg field of ModR/M selects a debug register + case AM_G: // reg field of ModR/M selects a general register + case AM_P: // reg field of ModR/M selects an MMX register + case AM_R: // mod field of ModR/M may refer only to a general register + case AM_S: // reg field of ModR/M selects a segment register + case AM_T: // reg field of ModR/M selects a test register + case AM_V: // reg field of ModR/M selects a 128-bit XMM register + have_modrm_ = true; + break; + + // In these addressing modes, there is a ModR/M byte and it needs to be + // decoded. No other (e.g. immediate) params than indicated in ModR/M. + case AM_E: // Operand is either a general-purpose register or memory, + // specified by ModR/M byte + case AM_M: // ModR/M byte will refer only to memory + case AM_Q: // Operand is either an MMX register or memory (complex + // evaluation), specified by ModR/M byte + case AM_W: // Operand is either a 128-bit XMM register or memory (complex + // eval), specified by ModR/M byte + have_modrm_ = true; + should_decode_modrm_ = true; + break; + + // These addressing modes specify an immediate or an offset value + // directly, so we need to look at the operand type to see how many + // bytes. + case AM_I: // Immediate data. + case AM_J: // Jump to offset. + case AM_O: // Operand is at offset. + switch (flag_operand & OT_MASK) { + case OT_B: // Byte regardless of operand-size attribute. + operand_bytes_ += OS_BYTE; + break; + case OT_C: // Byte or word, depending on operand-size attribute. + if (operand_is_32_bits_) + operand_bytes_ += OS_WORD; + else + operand_bytes_ += OS_BYTE; + break; + case OT_D: // Doubleword, regardless of operand-size attribute. + operand_bytes_ += OS_DOUBLE_WORD; + break; + case OT_DQ: // Double-quadword, regardless of operand-size attribute. + operand_bytes_ += OS_DOUBLE_QUAD_WORD; + break; + case OT_P: // 32-bit or 48-bit pointer, depending on operand-size + // attribute. + if (operand_is_32_bits_) + operand_bytes_ += OS_48_BIT_POINTER; + else + operand_bytes_ += OS_32_BIT_POINTER; + break; + case OT_PS: // 128-bit packed single-precision floating-point data. + operand_bytes_ += OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING; + break; + case OT_Q: // Quadword, regardless of operand-size attribute. + operand_bytes_ += OS_QUAD_WORD; + break; + case OT_S: // 6-byte pseudo-descriptor. + operand_bytes_ += OS_PSEUDO_DESCRIPTOR; + break; + case OT_SD: // Scalar Double-Precision Floating-Point Value + case OT_PD: // Unaligned packed double-precision floating point value + operand_bytes_ += OS_DOUBLE_PRECISION_FLOATING; + break; + case OT_SS: + // Scalar element of a 128-bit packed single-precision + // floating data. + // We simply return enItUnknown since we don't have to support + // floating point + succeeded = false; + break; + case OT_V: // Word or doubleword, depending on operand-size attribute. + if (operand_is_32_bits_) + operand_bytes_ += OS_DOUBLE_WORD; + else + operand_bytes_ += OS_WORD; + break; + case OT_W: // Word, regardless of operand-size attribute. + operand_bytes_ += OS_WORD; + break; + + // Can safely ignore these. + case OT_A: // Two one-word operands in memory or two double-word + // operands in memory + case OT_PI: // Quadword MMX technology register (e.g. mm0) + case OT_SI: // Doubleword integer register (e.g., eax) + break; + + default: + break; + } + break; + + default: + break; + } + + return succeeded; +} + +bool MiniDisassembler::ProcessModrm(unsigned char* start_byte, + unsigned int* size) { + // If we don't need to decode, we just return the size of the ModR/M + // byte (there is never a SIB byte in this case). + if (!should_decode_modrm_) { + (*size)++; + return true; + } + + // We never care about the reg field, only the combination of the mod + // and r/m fields, so let's start by packing those fields together into + // 5 bits. + unsigned char modrm = (*start_byte); + unsigned char mod = modrm & 0xC0; // mask out top two bits to get mod field + modrm = modrm & 0x07; // mask out bottom 3 bits to get r/m field + mod = mod >> 3; // shift the mod field to the right place + modrm = mod | modrm; // combine the r/m and mod fields as discussed + mod = mod >> 3; // shift the mod field to bits 2..0 + + // Invariant: modrm contains the mod field in bits 4..3 and the r/m field + // in bits 2..0, and mod contains the mod field in bits 2..0 + + const ModrmEntry* modrm_entry = 0; + if (address_is_32_bits_) + modrm_entry = &s_ia32_modrm_map_[modrm]; + else + modrm_entry = &s_ia16_modrm_map_[modrm]; + + // Invariant: modrm_entry points to information that we need to decode + // the ModR/M byte. + + // Add to the count of operand bytes, if the ModR/M byte indicates + // that some operands are encoded in the instruction. + if (modrm_entry->is_encoded_in_instruction_) + operand_bytes_ += modrm_entry->operand_size_; + + // Process the SIB byte if necessary, and return the count + // of ModR/M and SIB bytes. + if (modrm_entry->use_sib_byte_) { + (*size)++; + return ProcessSib(start_byte + 1, mod, size); + } else { + (*size)++; + return true; + } +} + +bool MiniDisassembler::ProcessSib(unsigned char* start_byte, + unsigned char mod, + unsigned int* size) { + // get the mod field from the 2..0 bits of the SIB byte + unsigned char sib_base = (*start_byte) & 0x07; + if (0x05 == sib_base) { + switch (mod) { + case 0x00: // mod == 00 + case 0x02: // mod == 10 + operand_bytes_ += OS_DOUBLE_WORD; + break; + case 0x01: // mod == 01 + operand_bytes_ += OS_BYTE; + break; + case 0x03: // mod == 11 + // According to the IA-32 docs, there does not seem to be a disp + // value for this value of mod + default: + break; + } + } + + (*size)++; + return true; +} + +}; // namespace sidestep diff --git a/sandbox/win/src/sidestep/mini_disassembler.h b/sandbox/win/src/sidestep/mini_disassembler.h new file mode 100644 index 0000000..444df36 --- /dev/null +++ b/sandbox/win/src/sidestep/mini_disassembler.h @@ -0,0 +1,156 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Definition of MiniDisassembler. + +#ifndef SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_H__ +#define SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_H__ + +#include "sandbox/src/sidestep/mini_disassembler_types.h" + +namespace sidestep { + +// This small disassembler is very limited +// in its functionality, and in fact does only the bare minimum required by the +// preamble patching utility. It may be useful for other purposes, however. +// +// The limitations include at least the following: +// -# No support for coprocessor opcodes, MMX, etc. +// -# No machine-readable identification of opcodes or decoding of +// assembly parameters. The name of the opcode (as a string) is given, +// however, to aid debugging. +// +// You may ask what this little disassembler actually does, then? The answer is +// that it does the following, which is exactly what the patching utility needs: +// -# Indicates if opcode is a jump (any kind) or a return (any kind) +// because this is important for the patching utility to determine if +// a function is too short or there are jumps too early in it for it +// to be preamble patched. +// -# The opcode length is always calculated, so that the patching utility +// can figure out where the next instruction starts, and whether it +// already has enough instructions to replace with the absolute jump +// to the patching code. +// +// The usage is quite simple; just create a MiniDisassembler and use its +// Disassemble() method. +// +// If you would like to extend this disassembler, please refer to the +// IA-32 Intel Architecture Software Developer's Manual Volume 2: +// Instruction Set Reference for information about operand decoding +// etc. +class MiniDisassembler { + public: + + // Creates a new instance and sets defaults. + // + // operand_default_32_bits: If true, the default operand size is + // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits. + // address_default_32_bits: If true, the default address size is + // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits. + MiniDisassembler(bool operand_default_32_bits, + bool address_default_32_bits); + + // Equivalent to MiniDisassembler(true, true); + MiniDisassembler(); + + // Attempts to disassemble a single instruction starting from the + // address in memory it is pointed to. + // + // start: Address where disassembly should start. + // instruction_bytes: Variable that will be incremented by + // the length in bytes of the instruction. + // Returns enItJump, enItReturn or enItGeneric on success. enItUnknown + // if unable to disassemble, enItUnused if this seems to be an unused + // opcode. In the last two (error) cases, cbInstruction will be set + // to 0xffffffff. + // + // Postcondition: This instance of the disassembler is ready to be used again, + // with unchanged defaults from creation time. + InstructionType Disassemble(unsigned char* start, + unsigned int* instruction_bytes); + + private: + + // Makes the disassembler ready for reuse. + void Initialize(); + + // Sets the flags for address and operand sizes. + // Returns Number of prefix bytes. + InstructionType ProcessPrefixes(unsigned char* start, unsigned int* size); + + // Sets the flag for whether we have ModR/M, and increments + // operand_bytes_ if any are specifies by the opcode directly. + // Returns Number of opcode bytes. + InstructionType ProcessOpcode(unsigned char* start, + unsigned int table, + unsigned int* size); + + // Checks the type of the supplied operand. Increments + // operand_bytes_ if it directly indicates an immediate etc. + // operand. Asserts have_modrm_ if the operand specifies + // a ModR/M byte. + bool ProcessOperand(int flag_operand); + + // Increments operand_bytes_ by size specified by ModR/M and + // by SIB if present. + // Returns 0 in case of error, 1 if there is just a ModR/M byte, + // 2 if there is a ModR/M byte and a SIB byte. + bool ProcessModrm(unsigned char* start, unsigned int* size); + + // Processes the SIB byte that it is pointed to. + // start: Pointer to the SIB byte. + // mod: The mod field from the ModR/M byte. + // Returns 1 to indicate success (indicates 1 SIB byte) + bool ProcessSib(unsigned char* start, unsigned char mod, unsigned int* size); + + // The instruction type we have decoded from the opcode. + InstructionType instruction_type_; + + // Counts the number of bytes that is occupied by operands in + // the current instruction (note: we don't care about how large + // operands stored in registers etc. are). + unsigned int operand_bytes_; + + // True iff there is a ModR/M byte in this instruction. + bool have_modrm_; + + // True iff we need to decode the ModR/M byte (sometimes it just + // points to a register, we can tell by the addressing mode). + bool should_decode_modrm_; + + // Current operand size is 32 bits if true, 16 bits if false. + bool operand_is_32_bits_; + + // Default operand size is 32 bits if true, 16 bits if false. + bool operand_default_is_32_bits_; + + // Current address size is 32 bits if true, 16 bits if false. + bool address_is_32_bits_; + + // Default address size is 32 bits if true, 16 bits if false. + bool address_default_is_32_bits_; + + // Huge big opcode table based on the IA-32 manual, defined + // in Ia32OpcodeMap.cpp + static const OpcodeTable s_ia32_opcode_map_[]; + + // Somewhat smaller table to help with decoding ModR/M bytes + // when 16-bit addressing mode is being used. Defined in + // Ia32ModrmMap.cpp + static const ModrmEntry s_ia16_modrm_map_[]; + + // Somewhat smaller table to help with decoding ModR/M bytes + // when 32-bit addressing mode is being used. Defined in + // Ia32ModrmMap.cpp + static const ModrmEntry s_ia32_modrm_map_[]; + + // Indicators of whether we got certain prefixes that certain + // silly Intel instructions depend on in nonstandard ways for + // their behaviors. + bool got_f2_prefix_, got_f3_prefix_, got_66_prefix_; +}; + +}; // namespace sidestep + +#endif // SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_H__ diff --git a/sandbox/win/src/sidestep/mini_disassembler_types.h b/sandbox/win/src/sidestep/mini_disassembler_types.h new file mode 100644 index 0000000..1c10626 --- /dev/null +++ b/sandbox/win/src/sidestep/mini_disassembler_types.h @@ -0,0 +1,197 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Several simple types used by the disassembler and some of the patching +// mechanisms. + +#ifndef SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H__ +#define SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H__ + +namespace sidestep { + +// Categories of instructions that we care about +enum InstructionType { + // This opcode is not used + IT_UNUSED, + // This disassembler does not recognize this opcode (error) + IT_UNKNOWN, + // This is not an instruction but a reference to another table + IT_REFERENCE, + // This byte is a prefix byte that we can ignore + IT_PREFIX, + // This is a prefix byte that switches to the nondefault address size + IT_PREFIX_ADDRESS, + // This is a prefix byte that switches to the nondefault operand size + IT_PREFIX_OPERAND, + // A jump or call instruction + IT_JUMP, + // A return instruction + IT_RETURN, + // Any other type of instruction (in this case we don't care what it is) + IT_GENERIC, +}; + +// Lists IA-32 operand sizes in multiples of 8 bits +enum OperandSize { + OS_ZERO = 0, + OS_BYTE = 1, + OS_WORD = 2, + OS_DOUBLE_WORD = 4, + OS_QUAD_WORD = 8, + OS_DOUBLE_QUAD_WORD = 16, + OS_32_BIT_POINTER = 32/8, + OS_48_BIT_POINTER = 48/8, + OS_SINGLE_PRECISION_FLOATING = 32/8, + OS_DOUBLE_PRECISION_FLOATING = 64/8, + OS_DOUBLE_EXTENDED_PRECISION_FLOATING = 80/8, + OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING = 128/8, + OS_PSEUDO_DESCRIPTOR = 6 +}; + +// Operand addressing methods from the IA-32 manual. The enAmMask value +// is a mask for the rest. The other enumeration values are named for the +// names given to the addressing methods in the manual, e.g. enAm_D is for +// the D addressing method. +// +// The reason we use a full 4 bytes and a mask, is that we need to combine +// these flags with the enOperandType to store the details +// on the operand in a single integer. +enum AddressingMethod { + AM_NOT_USED = 0, // This operand is not used for this instruction + AM_MASK = 0x00FF0000, // Mask for the rest of the values in this enumeration + AM_A = 0x00010000, // A addressing type + AM_C = 0x00020000, // C addressing type + AM_D = 0x00030000, // D addressing type + AM_E = 0x00040000, // E addressing type + AM_F = 0x00050000, // F addressing type + AM_G = 0x00060000, // G addressing type + AM_I = 0x00070000, // I addressing type + AM_J = 0x00080000, // J addressing type + AM_M = 0x00090000, // M addressing type + AM_O = 0x000A0000, // O addressing type + AM_P = 0x000B0000, // P addressing type + AM_Q = 0x000C0000, // Q addressing type + AM_R = 0x000D0000, // R addressing type + AM_S = 0x000E0000, // S addressing type + AM_T = 0x000F0000, // T addressing type + AM_V = 0x00100000, // V addressing type + AM_W = 0x00110000, // W addressing type + AM_X = 0x00120000, // X addressing type + AM_Y = 0x00130000, // Y addressing type + AM_REGISTER = 0x00140000, // Specific register is always used as this op + AM_IMPLICIT = 0x00150000, // An implicit, fixed value is used +}; + +// Operand types from the IA-32 manual. The enOtMask value is +// a mask for the rest. The rest of the values are named for the +// names given to these operand types in the manual, e.g. enOt_ps +// is for the ps operand type in the manual. +// +// The reason we use a full 4 bytes and a mask, is that we need +// to combine these flags with the enAddressingMethod to store the details +// on the operand in a single integer. +enum OperandType { + OT_MASK = 0xFF000000, + OT_A = 0x01000000, + OT_B = 0x02000000, + OT_C = 0x03000000, + OT_D = 0x04000000, + OT_DQ = 0x05000000, + OT_P = 0x06000000, + OT_PI = 0x07000000, + OT_PS = 0x08000000, // actually unsupported for (we don't know its size) + OT_Q = 0x09000000, + OT_S = 0x0A000000, + OT_SS = 0x0B000000, + OT_SI = 0x0C000000, + OT_V = 0x0D000000, + OT_W = 0x0E000000, + OT_SD = 0x0F000000, // scalar double-precision floating-point value + OT_PD = 0x10000000, // double-precision floating point + // dummy "operand type" for address mode M - which doesn't specify + // operand type + OT_ADDRESS_MODE_M = 0x80000000 +}; + +// Everything that's in an Opcode (see below) except the three +// alternative opcode structs for different prefixes. +struct SpecificOpcode { + // Index to continuation table, or 0 if this is the last + // byte in the opcode. + int table_index_; + + // The opcode type + InstructionType type_; + + // Description of the type of the dest, src and aux operands, + // put together from an enOperandType flag and an enAddressingMethod + // flag. + int flag_dest_; + int flag_source_; + int flag_aux_; + + // We indicate the mnemonic for debugging purposes + const char* mnemonic_; +}; + +// The information we keep in our tables about each of the different +// valid instructions recognized by the IA-32 architecture. +struct Opcode { + // Index to continuation table, or 0 if this is the last + // byte in the opcode. + int table_index_; + + // The opcode type + InstructionType type_; + + // Description of the type of the dest, src and aux operands, + // put together from an enOperandType flag and an enAddressingMethod + // flag. + int flag_dest_; + int flag_source_; + int flag_aux_; + + // We indicate the mnemonic for debugging purposes + const char* mnemonic_; + + // Alternative opcode info if certain prefixes are specified. + // In most cases, all of these are zeroed-out. Only used if + // bPrefixDependent is true. + bool is_prefix_dependent_; + SpecificOpcode opcode_if_f2_prefix_; + SpecificOpcode opcode_if_f3_prefix_; + SpecificOpcode opcode_if_66_prefix_; +}; + +// Information about each table entry. +struct OpcodeTable { + // Table of instruction entries + const Opcode* table_; + // How many bytes left to shift ModR/M byte before applying mask + unsigned char shift_; + // Mask to apply to byte being looked at before comparing to table + unsigned char mask_; + // Minimum/maximum indexes in table. + unsigned char min_lim_; + unsigned char max_lim_; +}; + +// Information about each entry in table used to decode ModR/M byte. +struct ModrmEntry { + // Is the operand encoded as bytes in the instruction (rather than + // if it's e.g. a register in which case it's just encoded in the + // ModR/M byte) + bool is_encoded_in_instruction_; + + // Is there a SIB byte? In this case we always need to decode it. + bool use_sib_byte_; + + // What is the size of the operand (only important if it's encoded + // in the instruction)? + OperandSize operand_size_; +}; + +}; // namespace sidestep + +#endif // SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H__ diff --git a/sandbox/win/src/sidestep/preamble_patcher.h b/sandbox/win/src/sidestep/preamble_patcher.h new file mode 100644 index 0000000..18cf7f8 --- /dev/null +++ b/sandbox/win/src/sidestep/preamble_patcher.h @@ -0,0 +1,109 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Definition of PreamblePatcher + +#ifndef SANDBOX_SRC_SIDESTEP_PREAMBLE_PATCHER_H__ +#define SANDBOX_SRC_SIDESTEP_PREAMBLE_PATCHER_H__ + +namespace sidestep { + +// Maximum size of the preamble stub. We overwrite at least the first 5 +// bytes of the function. Considering the worst case scenario, we need 4 +// bytes + the max instruction size + 5 more bytes for our jump back to +// the original code. With that in mind, 32 is a good number :) +const size_t kMaxPreambleStubSize = 32; + +// Possible results of patching/unpatching +enum SideStepError { + SIDESTEP_SUCCESS = 0, + SIDESTEP_INVALID_PARAMETER, + SIDESTEP_INSUFFICIENT_BUFFER, + SIDESTEP_JUMP_INSTRUCTION, + SIDESTEP_FUNCTION_TOO_SMALL, + SIDESTEP_UNSUPPORTED_INSTRUCTION, + SIDESTEP_NO_SUCH_MODULE, + SIDESTEP_NO_SUCH_FUNCTION, + SIDESTEP_ACCESS_DENIED, + SIDESTEP_UNEXPECTED, +}; + +// Implements a patching mechanism that overwrites the first few bytes of +// a function preamble with a jump to our hook function, which is then +// able to call the original function via a specially-made preamble-stub +// that imitates the action of the original preamble. +// +// Note that there are a number of ways that this method of patching can +// fail. The most common are: +// - If there is a jump (jxx) instruction in the first 5 bytes of +// the function being patched, we cannot patch it because in the +// current implementation we do not know how to rewrite relative +// jumps after relocating them to the preamble-stub. Note that +// if you really really need to patch a function like this, it +// would be possible to add this functionality (but at some cost). +// - If there is a return (ret) instruction in the first 5 bytes +// we cannot patch the function because it may not be long enough +// for the jmp instruction we use to inject our patch. +// - If there is another thread currently executing within the bytes +// that are copied to the preamble stub, it will crash in an undefined +// way. +// +// If you get any other error than the above, you're either pointing the +// patcher at an invalid instruction (e.g. into the middle of a multi- +// byte instruction, or not at memory containing executable instructions) +// or, there may be a bug in the disassembler we use to find +// instruction boundaries. +class PreamblePatcher { + public: + // Patches target_function to point to replacement_function using a provided + // preamble_stub of stub_size bytes. + // Returns An error code indicating the result of patching. + template + static SideStepError Patch(T target_function, T replacement_function, + void* preamble_stub, size_t stub_size) { + return RawPatchWithStub(target_function, replacement_function, + reinterpret_cast(preamble_stub), + stub_size, NULL); + } + + private: + + // Patches a function by overwriting its first few bytes with + // a jump to a different function. This is similar to the RawPatch + // function except that it uses the stub allocated by the caller + // instead of allocating it. + // + // To use this function, you first have to call VirtualProtect to make the + // target function writable at least for the duration of the call. + // + // target_function: A pointer to the function that should be + // patched. + // + // replacement_function: A pointer to the function that should + // replace the target function. The replacement function must have + // exactly the same calling convention and parameters as the original + // function. + // + // preamble_stub: A pointer to a buffer where the preamble stub + // should be copied. The size of the buffer should be sufficient to + // hold the preamble bytes. + // + // stub_size: Size in bytes of the buffer allocated for the + // preamble_stub + // + // bytes_needed: Pointer to a variable that receives the minimum + // number of bytes required for the stub. Can be set to NULL if you're + // not interested. + // + // Returns An error code indicating the result of patching. + static SideStepError RawPatchWithStub(void* target_function, + void *replacement_function, + unsigned char* preamble_stub, + size_t stub_size, + size_t* bytes_needed); +}; + +}; // namespace sidestep + +#endif // SANDBOX_SRC_SIDESTEP_PREAMBLE_PATCHER_H__ diff --git a/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp b/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp new file mode 100644 index 0000000..4bdd79c2 --- /dev/null +++ b/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp @@ -0,0 +1,179 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Implementation of PreamblePatcher + +#include "sandbox/src/sidestep/preamble_patcher.h" + +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sidestep/mini_disassembler.h" + +// Definitions of assembly statements we need +#define ASM_JMP32REL 0xE9 +#define ASM_INT3 0xCC + +namespace { + +// Very basic memcpy. We are copying 4 to 12 bytes most of the time, so there +// is no attempt to optimize this code or have a general purpose function. +// We don't want to call the crt from this code. +inline void* RawMemcpy(void* destination, const void* source, size_t bytes) { + const char* from = reinterpret_cast(source); + char* to = reinterpret_cast(destination); + + for (size_t i = 0; i < bytes ; i++) + to[i] = from[i]; + + return destination; +} + +// Very basic memset. We are filling 1 to 7 bytes most of the time, so there +// is no attempt to optimize this code or have a general purpose function. +// We don't want to call the crt from this code. +inline void* RawMemset(void* destination, int value, size_t bytes) { + char* to = reinterpret_cast(destination); + + for (size_t i = 0; i < bytes ; i++) + to[i] = static_cast(value); + + return destination; +} + +} // namespace + +#define ASSERT(a, b) DCHECK_NT(a) + +namespace sidestep { + +SideStepError PreamblePatcher::RawPatchWithStub( + void* target_function, + void* replacement_function, + unsigned char* preamble_stub, + size_t stub_size, + size_t* bytes_needed) { + if ((NULL == target_function) || + (NULL == replacement_function) || + (NULL == preamble_stub)) { + ASSERT(false, (L"Invalid parameters - either pTargetFunction or " + L"pReplacementFunction or pPreambleStub were NULL.")); + return SIDESTEP_INVALID_PARAMETER; + } + + // TODO(V7:joi) Siggi and I just had a discussion and decided that both + // patching and unpatching are actually unsafe. We also discussed a + // method of making it safe, which is to freeze all other threads in the + // process, check their thread context to see if their eip is currently + // inside the block of instructions we need to copy to the stub, and if so + // wait a bit and try again, then unfreeze all threads once we've patched. + // Not implementing this for now since we're only using SideStep for unit + // testing, but if we ever use it for production code this is what we + // should do. + // + // NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using + // FPU instructions, and on newer processors we could use cmpxchg8b or + // cmpxchg16b. So it might be possible to do the patching/unpatching + // atomically and avoid having to freeze other threads. Note though, that + // doing it atomically does not help if one of the other threads happens + // to have its eip in the middle of the bytes you change while you change + // them. + unsigned char* target = reinterpret_cast(target_function); + + // Let's disassemble the preamble of the target function to see if we can + // patch, and to see how much of the preamble we need to take. We need 5 + // bytes for our jmp instruction, so let's find the minimum number of + // instructions to get 5 bytes. + MiniDisassembler disassembler; + unsigned int preamble_bytes = 0; + while (preamble_bytes < 5) { + InstructionType instruction_type = + disassembler.Disassemble(target + preamble_bytes, &preamble_bytes); + if (IT_JUMP == instruction_type) { + ASSERT(false, (L"Unable to patch because there is a jump instruction " + L"in the first 5 bytes.")); + return SIDESTEP_JUMP_INSTRUCTION; + } else if (IT_RETURN == instruction_type) { + ASSERT(false, (L"Unable to patch because function is too short")); + return SIDESTEP_FUNCTION_TOO_SMALL; + } else if (IT_GENERIC != instruction_type) { + ASSERT(false, (L"Disassembler encountered unsupported instruction " + L"(either unused or unknown")); + return SIDESTEP_UNSUPPORTED_INSTRUCTION; + } + } + + if (NULL != bytes_needed) + *bytes_needed = preamble_bytes + 5; + + // Inv: preamble_bytes is the number of bytes (at least 5) that we need to + // take from the preamble to have whole instructions that are 5 bytes or more + // in size total. The size of the stub required is cbPreamble + size of + // jmp (5) + if (preamble_bytes + 5 > stub_size) { + NOTREACHED_NT(); + return SIDESTEP_INSUFFICIENT_BUFFER; + } + + // First, copy the preamble that we will overwrite. + RawMemcpy(reinterpret_cast(preamble_stub), + reinterpret_cast(target), preamble_bytes); + + // Now, make a jmp instruction to the rest of the target function (minus the + // preamble bytes we moved into the stub) and copy it into our preamble-stub. + // find address to jump to, relative to next address after jmp instruction +#pragma warning(push) +#pragma warning(disable:4244) + // This assignment generates a warning because it is 32 bit specific. + int relative_offset_to_target_rest + = ((reinterpret_cast(target) + preamble_bytes) - + (preamble_stub + preamble_bytes + 5)); +#pragma warning(pop) + // jmp (Jump near, relative, displacement relative to next instruction) + preamble_stub[preamble_bytes] = ASM_JMP32REL; + // copy the address + RawMemcpy(reinterpret_cast(preamble_stub + preamble_bytes + 1), + reinterpret_cast(&relative_offset_to_target_rest), 4); + + // Inv: preamble_stub points to assembly code that will execute the + // original function by first executing the first cbPreamble bytes of the + // preamble, then jumping to the rest of the function. + + // Overwrite the first 5 bytes of the target function with a jump to our + // replacement function. + // (Jump near, relative, displacement relative to next instruction) + target[0] = ASM_JMP32REL; + + // Find offset from instruction after jmp, to the replacement function. +#pragma warning(push) +#pragma warning(disable:4244) + int offset_to_replacement_function = + reinterpret_cast(replacement_function) - + reinterpret_cast(target) - 5; +#pragma warning(pop) + // complete the jmp instruction + RawMemcpy(reinterpret_cast(target + 1), + reinterpret_cast(&offset_to_replacement_function), 4); + // Set any remaining bytes that were moved to the preamble-stub to INT3 so + // as not to cause confusion (otherwise you might see some strange + // instructions if you look at the disassembly, or even invalid + // instructions). Also, by doing this, we will break into the debugger if + // some code calls into this portion of the code. If this happens, it + // means that this function cannot be patched using this patcher without + // further thought. + if (preamble_bytes > 5) { + RawMemset(reinterpret_cast(target + 5), ASM_INT3, + preamble_bytes - 5); + } + + // Inv: The memory pointed to by target_function now points to a relative + // jump instruction that jumps over to the preamble_stub. The preamble + // stub contains the first stub_size bytes of the original target + // function's preamble code, followed by a relative jump back to the next + // instruction after the first cbPreamble bytes. + + return SIDESTEP_SUCCESS; +} + +}; // namespace sidestep + +#undef ASSERT diff --git a/sandbox/win/src/sidestep_resolver.cc b/sandbox/win/src/sidestep_resolver.cc new file mode 100644 index 0000000..e22ca6b --- /dev/null +++ b/sandbox/win/src/sidestep_resolver.cc @@ -0,0 +1,200 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/sidestep_resolver.h" + +#include "base/win/pe_image.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sidestep/preamble_patcher.h" + +namespace { + +const size_t kSizeOfSidestepStub = sidestep::kMaxPreambleStubSize; + +struct SidestepThunk { + char sidestep[kSizeOfSidestepStub]; // Storage for the sidestep stub. + int internal_thunk; // Dummy member to the beginning of the internal thunk. +}; + +struct SmartThunk { + const void* module_base; // Target module's base. + const void* interceptor; // Real interceptor. + SidestepThunk sidestep; // Standard sidestep thunk. +}; + +} // namespace + +namespace sandbox { + +NTSTATUS SidestepResolverThunk::Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used) { + NTSTATUS ret = Init(target_module, interceptor_module, target_name, + interceptor_name, interceptor_entry_point, + thunk_storage, storage_bytes); + if (!NT_SUCCESS(ret)) + return ret; + + SidestepThunk* thunk = reinterpret_cast(thunk_storage); + + size_t internal_bytes = storage_bytes - kSizeOfSidestepStub; + if (!SetInternalThunk(&thunk->internal_thunk, internal_bytes, thunk_storage, + interceptor_)) + return STATUS_BUFFER_TOO_SMALL; + + AutoProtectMemory memory; + memory.ChangeProtection(target_, kSizeOfSidestepStub, PAGE_READWRITE); + + sidestep::SideStepError rv = sidestep::PreamblePatcher::Patch( + target_, reinterpret_cast(&thunk->internal_thunk), thunk_storage, + kSizeOfSidestepStub); + + if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER == rv) + return STATUS_BUFFER_TOO_SMALL; + + if (sidestep::SIDESTEP_SUCCESS != rv) + return STATUS_UNSUCCESSFUL; + + if (storage_used) + *storage_used = GetThunkSize(); + + return ret; +} + +size_t SidestepResolverThunk::GetThunkSize() const { + return GetInternalThunkSize() + kSizeOfSidestepStub; +} + +// This is basically a wrapper around the normal sidestep patch that extends +// the thunk to use a chained interceptor. It uses the fact that +// SetInternalThunk generates the code to pass as the first parameter whatever +// it receives as original_function; we let SidestepResolverThunk set this value +// to its saved code, and then we change it to our thunk data. +NTSTATUS SmartSidestepResolverThunk::Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used) { + if (storage_bytes < GetThunkSize()) + return STATUS_BUFFER_TOO_SMALL; + + SmartThunk* thunk = reinterpret_cast(thunk_storage); + thunk->module_base = target_module; + + NTSTATUS ret; + if (interceptor_entry_point) { + thunk->interceptor = interceptor_entry_point; + } else { + ret = ResolveInterceptor(interceptor_module, interceptor_name, + &thunk->interceptor); + if (!NT_SUCCESS(ret)) + return ret; + } + + // Perform a standard sidestep patch on the last part of the thunk, but point + // to our internal smart interceptor. + size_t standard_bytes = storage_bytes - offsetof(SmartThunk, sidestep); + ret = SidestepResolverThunk::Setup(target_module, interceptor_module, + target_name, NULL, &SmartStub, + &thunk->sidestep, standard_bytes, NULL); + if (!NT_SUCCESS(ret)) + return ret; + + // Fix the internal thunk to pass the whole buffer to the interceptor. + SetInternalThunk(&thunk->sidestep.internal_thunk, GetInternalThunkSize(), + thunk_storage, &SmartStub); + + if (storage_used) + *storage_used = GetThunkSize(); + + return ret; +} + +size_t SmartSidestepResolverThunk::GetThunkSize() const { + return GetInternalThunkSize() + kSizeOfSidestepStub + + offsetof(SmartThunk, sidestep); +} + +// This code must basically either call the intended interceptor or skip the +// call and invoke instead the original function. In any case, we are saving +// the registers that may be trashed by our c++ code. +// +// This function is called with a first parameter inserted by us, that points +// to our SmartThunk. When we call the interceptor we have to replace this +// parameter with the one expected by that function (stored inside our +// structure); on the other hand, when we skip the interceptor we have to remove +// that extra argument before calling the original function. +// +// When we skip the interceptor, the transformation of the stack looks like: +// On Entry: On Use: On Exit: +// [param 2] = first real argument [param 2] (esp+1c) [param 2] +// [param 1] = our SmartThunk [param 1] (esp+18) [ret address] +// [ret address] = real caller [ret address] (esp+14) [xxx] +// [xxx] [addr to jump to] (esp+10) [xxx] +// [xxx] [saved eax] [xxx] +// [xxx] [saved ebx] [xxx] +// [xxx] [saved ecx] [xxx] +// [xxx] [saved edx] [xxx] +__declspec(naked) +void SmartSidestepResolverThunk::SmartStub() { + __asm { + push eax // Space for the jump. + push eax // Save registers. + push ebx + push ecx + push edx + mov ebx, [esp + 0x18] // First parameter = SmartThunk. + mov edx, [esp + 0x14] // Get the return address. + mov eax, [ebx]SmartThunk.module_base + push edx + push eax + call SmartSidestepResolverThunk::IsInternalCall + add esp, 8 + + test eax, eax + lea edx, [ebx]SmartThunk.sidestep // The original function. + jz call_interceptor + + // Skip this call + mov ecx, [esp + 0x14] // Return address. + mov [esp + 0x18], ecx // Remove first parameter. + mov [esp + 0x10], edx + pop edx // Restore registers. + pop ecx + pop ebx + pop eax + ret 4 // Jump to original function. + + call_interceptor: + mov ecx, [ebx]SmartThunk.interceptor + mov [esp + 0x18], edx // Replace first parameter. + mov [esp + 0x10], ecx + pop edx // Restore registers. + pop ecx + pop ebx + pop eax + ret // Jump to original function. + } +} + +bool SmartSidestepResolverThunk::IsInternalCall(const void* base, + void* return_address) { + DCHECK_NT(base); + DCHECK_NT(return_address); + + base::win::PEImage pe(base); + if (pe.GetImageSectionFromAddr(return_address)) + return true; + return false; +} + +} // namespace sandbox diff --git a/sandbox/win/src/sidestep_resolver.h b/sandbox/win/src/sidestep_resolver.h new file mode 100644 index 0000000..1e7fdac --- /dev/null +++ b/sandbox/win/src/sidestep_resolver.h @@ -0,0 +1,73 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SIDESTEP_RESOLVER_H__ +#define SANDBOX_SRC_SIDESTEP_RESOLVER_H__ + +#include "base/basictypes.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/resolver.h" + +namespace sandbox { + +// This is the concrete resolver used to perform sidestep interceptions. +class SidestepResolverThunk : public ResolverThunk { + public: + SidestepResolverThunk() {} + virtual ~SidestepResolverThunk() {} + + // Implementation of Resolver::Setup. + virtual NTSTATUS Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used); + + // Implementation of Resolver::GetThunkSize. + virtual size_t GetThunkSize() const; + + private: + DISALLOW_COPY_AND_ASSIGN(SidestepResolverThunk); +}; + +// This is the concrete resolver used to perform smart sidestep interceptions. +// This means basically a sidestep interception that skips the interceptor when +// the caller resides on the same dll being intercepted. It is intended as +// a helper only, because that determination is not infallible. +class SmartSidestepResolverThunk : public SidestepResolverThunk { + public: + SmartSidestepResolverThunk() {} + virtual ~SmartSidestepResolverThunk() {} + + // Implementation of Resolver::Setup. + virtual NTSTATUS Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used); + + // Implementation of Resolver::GetThunkSize. + virtual size_t GetThunkSize() const; + + private: + // Performs the actual call to the interceptor if the conditions are correct + // (as determined by IsInternalCall). + static void SmartStub(); + + // Returns true if return_address is inside the module loaded at base. + static bool IsInternalCall(const void* base, void* return_address); + + DISALLOW_COPY_AND_ASSIGN(SmartSidestepResolverThunk); +}; + +} // namespace sandbox + + +#endif // SANDBOX_SRC_SIDESTEP_RESOLVER_H__ diff --git a/sandbox/win/src/sync_dispatcher.cc b/sandbox/win/src/sync_dispatcher.cc new file mode 100644 index 0000000..025fd96 --- /dev/null +++ b/sandbox/win/src/sync_dispatcher.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/sync_dispatcher.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/interceptors.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_broker.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sync_interception.h" +#include "sandbox/src/sync_policy.h" + +namespace sandbox { + +SyncDispatcher::SyncDispatcher(PolicyBase* policy_base) + : policy_base_(policy_base) { + static const IPCCall create_params = { + {IPC_CREATEEVENT_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast(&SyncDispatcher::CreateEvent) + }; + + static const IPCCall open_params = { + {IPC_OPENEVENT_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast(&SyncDispatcher::OpenEvent) + }; + + ipc_calls_.push_back(create_params); + ipc_calls_.push_back(open_params); +} + +bool SyncDispatcher::SetupService(InterceptionManager* manager, + int service) { + if (IPC_CREATEEVENT_TAG == service) + return INTERCEPT_EAT(manager, L"kernel32.dll", CreateEventW, + CREATE_EVENT_ID, 20); + + if (IPC_OPENEVENT_TAG == service) + return INTERCEPT_EAT(manager, L"kernel32.dll", OpenEventW, + OPEN_EVENT_ID, 16); + + return false; +} + +bool SyncDispatcher::CreateEvent(IPCInfo* ipc, std::wstring* name, + DWORD manual_reset, DWORD initial_state) { + const wchar_t* event_name = name->c_str(); + CountedParameterSet params; + params[NameBased::NAME] = ParamPickerMake(event_name); + + EvalResult result = policy_base_->EvalPolicy(IPC_CREATEEVENT_TAG, + params.GetBase()); + HANDLE handle = NULL; + DWORD ret = SyncPolicy::CreateEventAction(result, *ipc->client_info, *name, + manual_reset, initial_state, + &handle); + // Return operation status on the IPC. + ipc->return_info.win32_result = ret; + ipc->return_info.handle = handle; + return true; +} + +bool SyncDispatcher::OpenEvent(IPCInfo* ipc, std::wstring* name, + DWORD desired_access, DWORD inherit_handle) { + const wchar_t* event_name = name->c_str(); + + CountedParameterSet params; + params[OpenEventParams::NAME] = ParamPickerMake(event_name); + params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access); + + EvalResult result = policy_base_->EvalPolicy(IPC_OPENEVENT_TAG, + params.GetBase()); + HANDLE handle = NULL; + DWORD ret = SyncPolicy::OpenEventAction(result, *ipc->client_info, *name, + desired_access, inherit_handle, + &handle); + // Return operation status on the IPC. + ipc->return_info.win32_result = ret; + ipc->return_info.handle = handle; + return true; +} + +} // namespace sandbox diff --git a/sandbox/win/src/sync_dispatcher.h b/sandbox/win/src/sync_dispatcher.h new file mode 100644 index 0000000..be9b9a2 --- /dev/null +++ b/sandbox/win/src/sync_dispatcher.h @@ -0,0 +1,38 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SYNC_DISPATCHER_H_ +#define SANDBOX_SRC_SYNC_DISPATCHER_H_ + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sandbox_policy_base.h" + +namespace sandbox { + +// This class handles sync-related IPC calls. +class SyncDispatcher : public Dispatcher { + public: + explicit SyncDispatcher(PolicyBase* policy_base); + ~SyncDispatcher() {} + + // Dispatcher interface. + virtual bool SetupService(InterceptionManager* manager, int service); + +private: + // Processes IPC requests coming from calls to CreateEvent in the target. + bool CreateEvent(IPCInfo* ipc, std::wstring* name, DWORD manual_reset, + DWORD initial_state); + + // Processes IPC requests coming from calls to OpenEvent in the target. + bool OpenEvent(IPCInfo* ipc, std::wstring* name, DWORD desired_access, + DWORD inherit_handle); + + PolicyBase* policy_base_; + DISALLOW_COPY_AND_ASSIGN(SyncDispatcher); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SYNC_DISPATCHER_H_ diff --git a/sandbox/win/src/sync_interception.cc b/sandbox/win/src/sync_interception.cc new file mode 100644 index 0000000..4832c79 --- /dev/null +++ b/sandbox/win/src/sync_interception.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/sync_interception.h" + +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/policy_target.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +HANDLE WINAPI TargetCreateEventW(CreateEventWFunction orig_CreateEvent, + LPSECURITY_ATTRIBUTES security_attributes, + BOOL manual_reset, BOOL initial_state, + LPCWSTR name) { + // Check if the process can create it first. + HANDLE handle = orig_CreateEvent(security_attributes, manual_reset, + initial_state, name); + DWORD original_error = ::GetLastError(); + if (NULL != handle) + return handle; + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return NULL; + + do { + if (security_attributes) + break; + + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + CountedParameterSet params; + params[NameBased::NAME] = ParamPickerMake(name); + + if (!QueryBroker(IPC_CREATEEVENT_TAG, params.GetBase())) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_CREATEEVENT_TAG, name, manual_reset, + initial_state, &answer); + + if (SBOX_ALL_OK != code) + break; + + ::SetLastError(answer.win32_result); + return answer.handle; + } while (false); + + ::SetLastError(original_error); + return NULL; +} + +// Interception of OpenEventW on the child process. +// It should never be called directly +HANDLE WINAPI TargetOpenEventW(OpenEventWFunction orig_OpenEvent, + ACCESS_MASK desired_access, BOOL inherit_handle, + LPCWSTR name) { + // Check if the process can open it first. + HANDLE handle = orig_OpenEvent(desired_access, inherit_handle, name); + DWORD original_error = ::GetLastError(); + if (NULL != handle) + return handle; + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return NULL; + + do { + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) + break; + + uint32 inherit_handle_ipc = inherit_handle; + CountedParameterSet params; + params[OpenEventParams::NAME] = ParamPickerMake(name); + params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access); + + if (!QueryBroker(IPC_OPENEVENT_TAG, params.GetBase())) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IPC_OPENEVENT_TAG, name, desired_access, + inherit_handle_ipc, &answer); + + if (SBOX_ALL_OK != code) + break; + + ::SetLastError(answer.win32_result); + return answer.handle; + } while (false); + + ::SetLastError(original_error); + return NULL; +} + +} // namespace sandbox diff --git a/sandbox/win/src/sync_interception.h b/sandbox/win/src/sync_interception.h new file mode 100644 index 0000000..4ac23e7 --- /dev/null +++ b/sandbox/win/src/sync_interception.h @@ -0,0 +1,41 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +#ifndef SANDBOX_SRC_SYNC_INTERCEPTION_H__ +#define SANDBOX_SRC_SYNC_INTERCEPTION_H__ + +namespace sandbox { + +extern "C" { + +typedef HANDLE (WINAPI *CreateEventWFunction) ( + LPSECURITY_ATTRIBUTES lpEventAttributes, + DWORD dwDesiredAccess, + BOOL bInheritHandle, + LPCWSTR lpName); + +typedef HANDLE (WINAPI *OpenEventWFunction) ( + BOOL bManualReset, + BOOL bInitialState, + LPCWSTR lpName); + +// Interception of CreateEvent on the child process. +SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW( + CreateEventWFunction orig_CreateEvent, + LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset, + BOOL initial_state, LPCWSTR name); + +// Interception of OpenEvent on the child process. +SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW( + OpenEventWFunction orig_OpenEvent, ACCESS_MASK desired_access, + BOOL inherit_handle, LPCWSTR name); + +} // extern "C" + +} // namespace sandbox + +#endif // SANDBOX_SRC_SYNC_INTERCEPTION_H__ diff --git a/sandbox/win/src/sync_policy.cc b/sandbox/win/src/sync_policy.cc new file mode 100644 index 0000000..364cf73 --- /dev/null +++ b/sandbox/win/src/sync_policy.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "sandbox/src/sync_policy.h" + +#include "base/logging.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_engine_opcodes.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/sandbox_utils.h" + +namespace sandbox { + +bool SyncPolicy::GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy) { + std::wstring mod_name(name); + if (mod_name.empty()) { + return false; + } + + if (TargetPolicy::EVENTS_ALLOW_ANY != semantics && + TargetPolicy::EVENTS_ALLOW_READONLY != semantics) { + // Other flags are not valid for sync policy yet. + NOTREACHED(); + return false; + } + + // Add the open rule. + EvalResult result = ASK_BROKER; + PolicyRule open(result); + + if (!open.AddStringMatch(IF, OpenEventParams::NAME, name, CASE_INSENSITIVE)) + return false; + + if (TargetPolicy::EVENTS_ALLOW_READONLY == semantics) { + // We consider all flags that are not known to be readonly as potentially + // used for write. + DWORD allowed_flags = SYNCHRONIZE | GENERIC_READ | READ_CONTROL; + DWORD restricted_flags = ~allowed_flags; + open.AddNumberMatch(IF_NOT, OpenEventParams::ACCESS, restricted_flags, AND); + } + + if (!policy->AddRule(IPC_OPENEVENT_TAG, &open)) + return false; + + // If it's not a read only, add the create rule. + if (TargetPolicy::EVENTS_ALLOW_READONLY != semantics) { + PolicyRule create(result); + if (!create.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) + return false; + + if (!policy->AddRule(IPC_CREATEEVENT_TAG, &create)) + return false; + } + + return true; +} + +DWORD SyncPolicy::CreateEventAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &event_name, + uint32 manual_reset, + uint32 initial_state, + HANDLE *handle) { + // The only action supported is ASK_BROKER which means create the requested + // file as specified. + if (ASK_BROKER != eval_result) + return false; + + HANDLE local_handle = ::CreateEvent(NULL, manual_reset, initial_state, + event_name.c_str()); + if (NULL == local_handle) + return ::GetLastError(); + + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, + client_info.process, handle, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(local_handle); + return ERROR_ACCESS_DENIED; + } + return ERROR_SUCCESS; +} + +DWORD SyncPolicy::OpenEventAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &event_name, + uint32 desired_access, + uint32 inherit_handle, + HANDLE *handle) { + // The only action supported is ASK_BROKER which means create the requested + // file as specified. + if (ASK_BROKER != eval_result) + return false; + + HANDLE local_handle = ::OpenEvent(desired_access, FALSE, + event_name.c_str()); + if (NULL == local_handle) + return ::GetLastError(); + + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, + client_info.process, handle, 0, inherit_handle, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + ::CloseHandle(local_handle); + return ERROR_ACCESS_DENIED; + } + return ERROR_SUCCESS; +} + +} // namespace sandbox diff --git a/sandbox/win/src/sync_policy.h b/sandbox/win/src/sync_policy.h new file mode 100644 index 0000000..6404f79 --- /dev/null +++ b/sandbox/win/src/sync_policy.h @@ -0,0 +1,51 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_SYNC_POLICY_H__ +#define SANDBOX_SRC_SYNC_POLICY_H__ + +#include + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/policy_low_level.h" +#include "sandbox/src/sandbox_policy.h" + +namespace sandbox { + +enum EvalResult; + +// This class centralizes most of the knowledge related to sync policy +class SyncPolicy { + public: + // Creates the required low-level policy rules to evaluate a high-level + // policy rule for sync calls, in particular open or create actions. + // name is the sync object name, semantics is the desired semantics for the + // open or create and policy is the policy generator to which the rules are + // going to be added. + static bool GenerateRules(const wchar_t* name, + TargetPolicy::Semantics semantics, + LowLevelPolicy* policy); + + // Performs the desired policy action on a request. + // client_info is the target process that is making the request and + // eval_result is the desired policy action to accomplish. + static DWORD CreateEventAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &event_name, + uint32 manual_reset, + uint32 initial_state, + HANDLE *handle); + static DWORD OpenEventAction(EvalResult eval_result, + const ClientInfo& client_info, + const std::wstring &event_name, + uint32 desired_access, + uint32 inherit_handle, + HANDLE *handle); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_SYNC_POLICY_H__ diff --git a/sandbox/win/src/sync_policy_test.cc b/sandbox/win/src/sync_policy_test.cc new file mode 100644 index 0000000..3c87243 --- /dev/null +++ b/sandbox/win/src/sync_policy_test.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/win/scoped_handle.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_policy.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/nt_internals.h" +#include "sandbox/tests/common/controller.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t **argv) { + if (argc != 2) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + DWORD desired_access = SYNCHRONIZE; + if (L'f' == argv[0][0]) + desired_access = EVENT_ALL_ACCESS; + + base::win::ScopedHandle event_open(::OpenEvent( + desired_access, FALSE, argv[1])); + DWORD error_open = ::GetLastError(); + + if (event_open.Get()) + return SBOX_TEST_SUCCEEDED; + + if (ERROR_ACCESS_DENIED == error_open || + ERROR_BAD_PATHNAME == error_open) + return SBOX_TEST_DENIED; + + return SBOX_TEST_FAILED; +} + +SBOX_TESTS_COMMAND int Event_CreateOpen(int argc, wchar_t **argv) { + if (argc < 2 || argc > 3) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + wchar_t *event_name = NULL; + if (3 == argc) + event_name = argv[2]; + + BOOL manual_reset = FALSE; + BOOL initial_state = FALSE; + if (L't' == argv[0][0]) + manual_reset = TRUE; + if (L't' == argv[1][0]) + initial_state = TRUE; + + base::win::ScopedHandle event_create(::CreateEvent( + NULL, manual_reset, initial_state, event_name)); + DWORD error_create = ::GetLastError(); + base::win::ScopedHandle event_open; + if (event_name) + event_open.Set(::OpenEvent(EVENT_ALL_ACCESS, FALSE, event_name)); + + if (event_create.Get()) { + DWORD wait = ::WaitForSingleObject(event_create.Get(), 0); + if (initial_state && WAIT_OBJECT_0 != wait) + return SBOX_TEST_FAILED; + + if (!initial_state && WAIT_TIMEOUT != wait) + return SBOX_TEST_FAILED; + } + + if (event_name) { + // Both event_open and event_create have to be valid. + if (event_open.Get() && event_create) + return SBOX_TEST_SUCCEEDED; + + if (event_open.Get() && !event_create || !event_open.Get() && event_create) + return SBOX_TEST_FAILED; + } else { + // Only event_create has to be valid. + if (event_create.Get()) + return SBOX_TEST_SUCCEEDED; + } + + if (ERROR_ACCESS_DENIED == error_create || + ERROR_BAD_PATHNAME == error_create) + return SBOX_TEST_DENIED; + + return SBOX_TEST_FAILED; +} + +// Tests the creation of events using all the possible combinations. +TEST(SyncPolicyTest, TestEvent) { + TestRunner runner; + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_ANY, + L"test1")); + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_ANY, + L"test2")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f test1")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f test2")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t test1")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t test2")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test3")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test4")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test3")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test4")); +} + +// Tests opening events with read only access. +TEST(SyncPolicyTest, TestEventReadOnly) { + TestRunner runner; + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_READONLY, + L"test1")); + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_READONLY, + L"test2")); + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_READONLY, + L"test5")); + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_READONLY, + L"test6")); + + base::win::ScopedHandle handle1(::CreateEvent(NULL, FALSE, FALSE, L"test1")); + base::win::ScopedHandle handle2(::CreateEvent(NULL, FALSE, FALSE, L"test2")); + base::win::ScopedHandle handle3(::CreateEvent(NULL, FALSE, FALSE, L"test3")); + base::win::ScopedHandle handle4(::CreateEvent(NULL, FALSE, FALSE, L"test4")); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test1")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_Open s test2")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test3")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open s test4")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test5")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test6")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test5")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test6")); +} + +} // namespace sandbox diff --git a/sandbox/win/src/target_interceptions.cc b/sandbox/win/src/target_interceptions.cc new file mode 100644 index 0000000..a1d463b --- /dev/null +++ b/sandbox/win/src/target_interceptions.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/target_interceptions.h" + +#include "sandbox/src/interception_agent.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_nt_util.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +SANDBOX_INTERCEPT NtExports g_nt; + +// Hooks NtMapViewOfSection to detect the load of DLLs. If hot patching is +// required for this dll, this functions patches it. +NTSTATUS WINAPI TargetNtMapViewOfSection( + NtMapViewOfSectionFunction orig_MapViewOfSection, HANDLE section, + HANDLE process, PVOID *base, ULONG_PTR zero_bits, SIZE_T commit_size, + PLARGE_INTEGER offset, PSIZE_T view_size, SECTION_INHERIT inherit, + ULONG allocation_type, ULONG protect) { + NTSTATUS ret = orig_MapViewOfSection(section, process, base, zero_bits, + commit_size, offset, view_size, inherit, + allocation_type, protect); + + static int s_load_count = 0; + if (1 == s_load_count) { + SandboxFactory::GetTargetServices()->GetState()->SetKernel32Loaded(); + s_load_count = 2; + } + + do { + if (!NT_SUCCESS(ret)) + break; + + if (!InitHeap()) + break; + + if (!IsSameProcess(process)) + break; + + if (!IsValidImageSection(section, base, offset, view_size)) + break; + + UINT image_flags; + UNICODE_STRING* module_name = + GetImageInfoFromModule(reinterpret_cast(*base), &image_flags); + UNICODE_STRING* file_name = GetBackingFilePath(*base); + + if ((!module_name) && (image_flags & MODULE_HAS_CODE)) { + // If the module has no exports we retrieve the module name from the + // full path of the mapped section. + module_name = ExtractModuleName(file_name); + } + + InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent(); + + if (agent) { + if (!agent->OnDllLoad(file_name, module_name, *base)) { + // Interception agent is demanding to un-map the module. + g_nt.UnmapViewOfSection(process, *base); + ret = STATUS_UNSUCCESSFUL; + } + } + + if (module_name) + operator delete(module_name, NT_ALLOC); + + if (file_name) + operator delete(file_name, NT_ALLOC); + + } while (false); + + if (!s_load_count) + s_load_count = 1; + + return ret; +} + +NTSTATUS WINAPI TargetNtUnmapViewOfSection( + NtUnmapViewOfSectionFunction orig_UnmapViewOfSection, HANDLE process, + PVOID base) { + NTSTATUS ret = orig_UnmapViewOfSection(process, base); + + if (!NT_SUCCESS(ret)) + return ret; + + if (!IsSameProcess(process)) + return ret; + + InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent(); + + if (agent) + agent->OnDllUnload(base); + + return ret; +} + +} // namespace sandbox diff --git a/sandbox/win/src/target_interceptions.h b/sandbox/win/src/target_interceptions.h new file mode 100644 index 0000000..dd6e12c --- /dev/null +++ b/sandbox/win/src/target_interceptions.h @@ -0,0 +1,35 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_types.h" + +#ifndef SANDBOX_SRC_TARGET_INTERCEPTIONS_H__ +#define SANDBOX_SRC_TARGET_INTERCEPTIONS_H__ + +namespace sandbox { + +extern "C" { + +// Interception of NtMapViewOfSection on the child process. +// It should never be called directly. This function provides the means to +// detect dlls being loaded, so we can patch them if needed. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtMapViewOfSection( + NtMapViewOfSectionFunction orig_MapViewOfSection, HANDLE section, + HANDLE process, PVOID *base, ULONG_PTR zero_bits, SIZE_T commit_size, + PLARGE_INTEGER offset, PSIZE_T view_size, SECTION_INHERIT inherit, + ULONG allocation_type, ULONG protect); + +// Interception of NtUnmapViewOfSection on the child process. +// It should never be called directly. This function provides the means to +// detect dlls being unloaded, so we can clean up our interceptions. +SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtUnmapViewOfSection( + NtUnmapViewOfSectionFunction orig_UnmapViewOfSection, HANDLE process, + PVOID base); + +} // extern "C" + +} // namespace sandbox + +#endif // SANDBOX_SRC_TARGET_INTERCEPTIONS_H__ diff --git a/sandbox/win/src/target_process.cc b/sandbox/win/src/target_process.cc new file mode 100644 index 0000000..88e2751 --- /dev/null +++ b/sandbox/win/src/target_process.cc @@ -0,0 +1,355 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/target_process.h" + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/win/pe_image.h" +#include "base/win/windows_version.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/policy_low_level.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/sharedmem_ipc_server.h" + +namespace { + +void CopyPolicyToTarget(const void* source, size_t size, void* dest) { + if (!source || !size) + return; + memcpy(dest, source, size); + sandbox::PolicyGlobal* policy = + reinterpret_cast(dest); + + size_t offset = reinterpret_cast(source); + + for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) { + size_t buffer = reinterpret_cast(policy->entry[i]); + if (buffer) { + buffer -= offset; + policy->entry[i] = reinterpret_cast(buffer); + } + } +} + +// Reserve a random range at the bottom of the address space in the target +// process to prevent predictable alocations at low addresses. +void PoisonLowerAddressRange(HANDLE process) { + unsigned int limit; + rand_s(&limit); + char* ptr = 0; + const size_t kMask64k = 0xFFFF; + // Random range (512k-16.5mb) in 64k steps. + const char* end = ptr + ((((limit % 16384) + 512) * 1024) & ~kMask64k); + while (ptr < end) { + MEMORY_BASIC_INFORMATION memory_info; + if (!::VirtualQueryEx(process, ptr, &memory_info, sizeof(memory_info))) + break; + size_t size = std::min((memory_info.RegionSize + kMask64k) & ~kMask64k, + static_cast(end - ptr)); + if (ptr && memory_info.State == MEM_FREE) + ::VirtualAllocEx(process, ptr, size, MEM_RESERVE, PAGE_NOACCESS); + ptr += size; + } +} + +} + +namespace sandbox { + +SANDBOX_INTERCEPT HANDLE g_shared_section; +SANDBOX_INTERCEPT size_t g_shared_IPC_size; +SANDBOX_INTERCEPT size_t g_shared_policy_size; + +// Returns the address of the main exe module in memory taking in account +// address space layout randomization. +void* GetBaseAddress(const wchar_t* exe_name, void* entry_point) { + HMODULE exe = ::LoadLibrary(exe_name); + if (NULL == exe) + return exe; + + base::win::PEImage pe(exe); + if (!pe.VerifyMagic()) { + ::FreeLibrary(exe); + return exe; + } + PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders(); + char* base = reinterpret_cast(entry_point) - + nt_header->OptionalHeader.AddressOfEntryPoint; + + ::FreeLibrary(exe); + return base; +} + + +TargetProcess::TargetProcess(HANDLE initial_token, HANDLE lockdown_token, + HANDLE job, ThreadProvider* thread_pool) + // This object owns everything initialized here except thread_pool and + // the job_ handle. The Job handle is closed by BrokerServices and results + // eventually in a call to our dtor. + : lockdown_token_(lockdown_token), + initial_token_(initial_token), + job_(job), + thread_pool_(thread_pool), + base_address_(NULL) { +} + +TargetProcess::~TargetProcess() { + DWORD exit_code = 0; + // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE + // will take effect only when the context changes. As far as the testing went, + // this wait was enough to switch context and kill the processes in the job. + // If this process is already dead, the function will return without waiting. + // TODO(nsylvain): If the process is still alive at the end, we should kill + // it. http://b/893891 + // For now, this wait is there only to do a best effort to prevent some leaks + // from showing up in purify. + ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50); + if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(), + &exit_code) || (STILL_ACTIVE == exit_code)) { + // It is an error to destroy this object while the target process is still + // alive because we need to destroy the IPC subsystem and cannot risk to + // have an IPC reach us after this point. + shared_section_.Take(); + SharedMemIPCServer* server = ipc_server_.release(); + sandbox_process_info_.TakeProcessHandle(); + return; + } + + // ipc_server_ references our process handle, so make sure the former is shut + // down before the latter is closed (by ScopedProcessInformation). + ipc_server_.reset(); +} + +// Creates the target (child) process suspended and assigns it to the job +// object. +DWORD TargetProcess::Create(const wchar_t* exe_path, + const wchar_t* command_line, + const wchar_t* desktop, + base::win::ScopedProcessInformation* target_info) { + exe_name_.reset(_wcsdup(exe_path)); + + // the command line needs to be writable by CreateProcess(). + scoped_ptr_malloc cmd_line(_wcsdup(command_line)); + scoped_ptr_malloc desktop_name(desktop ? _wcsdup(desktop) : NULL); + + // Start the target process suspended. + DWORD flags = + CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS; + + if (base::win::GetVersion() < base::win::VERSION_WIN8) { + // Windows 8 implements nested jobs, but for older systems we need to + // break out of any job we're in to enforce our restrictions. + flags |= CREATE_BREAKAWAY_FROM_JOB; + } + + STARTUPINFO startup_info = {sizeof(STARTUPINFO)}; + if (desktop) { + startup_info.lpDesktop = desktop_name.get(); + } + + base::win::ScopedProcessInformation process_info; + + if (!::CreateProcessAsUserW(lockdown_token_, + exe_path, + cmd_line.get(), + NULL, // No security attribute. + NULL, // No thread attribute. + FALSE, // Do not inherit handles. + flags, + NULL, // Use the environment of the caller. + NULL, // Use current directory of the caller. + &startup_info, + process_info.Receive())) { + return ::GetLastError(); + } + lockdown_token_.Close(); + + PoisonLowerAddressRange(process_info.process_handle()); + + DWORD win_result = ERROR_SUCCESS; + + // Assign the suspended target to the windows job object + if (!::AssignProcessToJobObject(job_, process_info.process_handle())) { + win_result = ::GetLastError(); + // It might be a security breach if we let the target run outside the job + // so kill it before it causes damage + ::TerminateProcess(process_info.process_handle(), 0); + return win_result; + } + + // Change the token of the main thread of the new process for the + // impersonation token with more rights. This allows the target to start; + // otherwise it will crash too early for us to help. + { + HANDLE temp_thread = process_info.thread_handle(); + if (!::SetThreadToken(&temp_thread, initial_token_)) { + win_result = ::GetLastError(); + ::TerminateProcess(process_info.process_handle(), 0); + return win_result; + } + initial_token_.Close(); + } + + CONTEXT context; + context.ContextFlags = CONTEXT_ALL; + if (!::GetThreadContext(process_info.thread_handle(), &context)) { + win_result = ::GetLastError(); + ::TerminateProcess(process_info.process_handle(), 0); + return win_result; + } + +#if defined(_WIN64) + void* entry_point = reinterpret_cast(context.Rcx); +#else +#pragma warning(push) +#pragma warning(disable: 4312) + // This cast generates a warning because it is 32 bit specific. + void* entry_point = reinterpret_cast(context.Eax); +#pragma warning(pop) +#endif // _WIN64 + + if (!target_info->DuplicateFrom(process_info)) { + win_result = ::GetLastError(); // This may or may not be correct. + ::TerminateProcess(process_info.process_handle(), 0); + return win_result; + } + + base_address_ = GetBaseAddress(exe_path, entry_point); + sandbox_process_info_.Swap(&process_info); + return win_result; +} + +ResultCode TargetProcess::TransferVariable(const char* name, void* address, + size_t size) { + if (!sandbox_process_info_.IsValid()) + return SBOX_ERROR_UNEXPECTED_CALL; + + void* child_var = address; + +#if SANDBOX_EXPORTS + HMODULE module = ::LoadLibrary(exe_name_.get()); + if (NULL == module) + return SBOX_ERROR_GENERIC; + + child_var = ::GetProcAddress(module, name); + ::FreeLibrary(module); + + if (NULL == child_var) + return SBOX_ERROR_GENERIC; + + size_t offset = reinterpret_cast(child_var) - + reinterpret_cast(module); + child_var = reinterpret_cast(MainModule()) + offset; +#else + UNREFERENCED_PARAMETER(name); +#endif + + SIZE_T written; + if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), + child_var, address, size, &written)) + return SBOX_ERROR_GENERIC; + + if (written != size) + return SBOX_ERROR_GENERIC; + + return SBOX_ALL_OK; +} + +// Construct the IPC server and the IPC dispatcher. When the target does +// an IPC it will eventually call the dispatcher. +DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy, + uint32 shared_IPC_size, uint32 shared_policy_size) { + // We need to map the shared memory on the target. This is necessary for + // any IPC that needs to take place, even if the target has not yet hit + // the main( ) function or even has initialized the CRT. So here we set + // the handle to the shared section. The target on the first IPC must do + // the rest, which boils down to calling MapViewofFile() + + // We use this single memory pool for IPC and for policy. + DWORD shared_mem_size = static_cast(shared_IPC_size + + shared_policy_size); + shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE | SEC_COMMIT, + 0, shared_mem_size, NULL)); + if (!shared_section_.IsValid()) { + return ::GetLastError(); + } + + DWORD access = FILE_MAP_READ | FILE_MAP_WRITE; + HANDLE target_shared_section; + if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_, + sandbox_process_info_.process_handle(), + &target_shared_section, access, FALSE, 0)) { + return ::GetLastError(); + } + + void* shared_memory = ::MapViewOfFile(shared_section_, + FILE_MAP_WRITE|FILE_MAP_READ, + 0, 0, 0); + if (NULL == shared_memory) { + return ::GetLastError(); + } + + CopyPolicyToTarget(policy, shared_policy_size, + reinterpret_cast(shared_memory) + shared_IPC_size); + + ResultCode ret; + // Set the global variables in the target. These are not used on the broker. + g_shared_section = target_shared_section; + ret = TransferVariable("g_shared_section", &g_shared_section, + sizeof(g_shared_section)); + g_shared_section = NULL; + if (SBOX_ALL_OK != ret) { + return (SBOX_ERROR_GENERIC == ret)? + ::GetLastError() : ERROR_INVALID_FUNCTION; + } + g_shared_IPC_size = shared_IPC_size; + ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size, + sizeof(g_shared_IPC_size)); + g_shared_IPC_size = 0; + if (SBOX_ALL_OK != ret) { + return (SBOX_ERROR_GENERIC == ret) ? + ::GetLastError() : ERROR_INVALID_FUNCTION; + } + g_shared_policy_size = shared_policy_size; + ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size, + sizeof(g_shared_policy_size)); + g_shared_policy_size = 0; + if (SBOX_ALL_OK != ret) { + return (SBOX_ERROR_GENERIC == ret) ? + ::GetLastError() : ERROR_INVALID_FUNCTION; + } + + ipc_server_.reset( + new SharedMemIPCServer(sandbox_process_info_.process_handle(), + sandbox_process_info_.process_id(), + job_, thread_pool_, ipc_dispatcher)); + + if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize)) + return ERROR_NOT_ENOUGH_MEMORY; + + // After this point we cannot use this handle anymore. + ::CloseHandle(sandbox_process_info_.TakeThreadHandle()); + + return ERROR_SUCCESS; +} + +void TargetProcess::Terminate() { + if (!sandbox_process_info_.IsValid()) + return; + + ::TerminateProcess(sandbox_process_info_.process_handle(), 0); +} + + +TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) { + TargetProcess* target = new TargetProcess(NULL, NULL, NULL, NULL); + target->sandbox_process_info_.Receive()->hProcess = process; + target->base_address_ = base_address; + return target; +} + +} // namespace sandbox diff --git a/sandbox/win/src/target_process.h b/sandbox/win/src/target_process.h new file mode 100644 index 0000000..6e462c3 --- /dev/null +++ b/sandbox/win/src/target_process.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_TARGET_PROCESS_H__ +#define SANDBOX_SRC_TARGET_PROCESS_H__ + +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/win/scoped_handle.h" +#include "base/win/scoped_process_information.h" +#include "sandbox/src/crosscall_server.h" +#include "sandbox/src/sandbox_types.h" + +namespace sandbox { + +class SharedMemIPCServer; +class ThreadProvider; + +// TargetProcess models a target instance (child process). Objects of this +// class are owned by the Policy used to create them. +class TargetProcess { + public: + // The constructor takes ownership of |initial_token| and |lockdown_token|. + TargetProcess(HANDLE initial_token, HANDLE lockdown_token, HANDLE job, + ThreadProvider* thread_pool); + ~TargetProcess(); + + // TODO(cpu): Currently there does not seem to be a reason to implement + // reference counting for this class since is internal, but kept the + // the same interface so the interception framework does not need to be + // touched at this point. + void AddRef() {} + void Release() {} + + // Creates the new target process. The process is created suspended. + DWORD Create(const wchar_t* exe_path, + const wchar_t* command_line, + const wchar_t* desktop, + base::win::ScopedProcessInformation* target_info); + + // Destroys the target process. + void Terminate(); + + // Creates the IPC objects such as the BrokerDispatcher and the + // IPC server. The IPC server uses the services of the thread_pool. + DWORD Init(Dispatcher* ipc_dispatcher, void* policy, + uint32 shared_IPC_size, uint32 shared_policy_size); + + // Returns the handle to the target process. + HANDLE Process() const { + return sandbox_process_info_.process_handle(); + } + + // Returns the handle to the job object that the target process belongs to. + HANDLE Job() const { + return job_; + } + + // Returns the address of the target main exe. This is used by the + // interceptions framework. + HMODULE MainModule() const { + return reinterpret_cast(base_address_); + } + + // Returns the name of the executable. + const wchar_t* Name() const { + return exe_name_.get(); + } + + // Returns the process id. + DWORD ProcessId() const { + return sandbox_process_info_.process_id(); + } + + // Returns the handle to the main thread. + HANDLE MainThread() const { + return sandbox_process_info_.thread_handle(); + } + + // Transfers a 32-bit variable between the broker and the target. + ResultCode TransferVariable(const char* name, void* address, size_t size); + + private: + // Details of the target process. + base::win::ScopedProcessInformation sandbox_process_info_; + // The token associated with the process. It provides the core of the + // sbox security. + base::win::ScopedHandle lockdown_token_; + // The token given to the initial thread so that the target process can + // start. It has more powers than the lockdown_token. + base::win::ScopedHandle initial_token_; + // Kernel handle to the shared memory used by the IPC server. + base::win::ScopedHandle shared_section_; + // Job object containing the target process. + HANDLE job_; + // Reference to the IPC subsystem. + scoped_ptr ipc_server_; + // Provides the threads used by the IPC. This class does not own this pointer. + ThreadProvider* thread_pool_; + // Base address of the main executable + void* base_address_; + // Full name of the target executable. + scoped_ptr_malloc exe_name_; + + // Function used for testing. + friend TargetProcess* MakeTestTargetProcess(HANDLE process, + HMODULE base_address); + + DISALLOW_IMPLICIT_CONSTRUCTORS(TargetProcess); +}; + +// Creates a mock TargetProcess used for testing interceptions. +// TODO(cpu): It seems that this method is not going to be used anymore. +TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address); + + +} // namespace sandbox + +#endif // SANDBOX_SRC_TARGET_PROCESS_H__ diff --git a/sandbox/win/src/target_services.cc b/sandbox/win/src/target_services.cc new file mode 100644 index 0000000..e13a3d6 --- /dev/null +++ b/sandbox/win/src/target_services.cc @@ -0,0 +1,188 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/target_services.h" + +#include + +#include "base/basictypes.h" +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/handle_closer_agent.h" +#include "sandbox/src/handle_interception.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/restricted_token_utils.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_types.h" +#include "sandbox/src/sharedmem_ipc_client.h" +#include "sandbox/src/sandbox_nt_util.h" + +namespace { + +// Flushing a cached key is triggered by just opening the key and closing the +// resulting handle. RegDisablePredefinedCache() is the documented way to flush +// HKCU so do not use it with this function. +bool FlushRegKey(HKEY root) { + HKEY key; + if (ERROR_SUCCESS == ::RegOpenKeyExW(root, NULL, 0, MAXIMUM_ALLOWED, &key)) { + if (ERROR_SUCCESS != ::RegCloseKey(key)) + return false; + } + return true; +} + +// This function forces advapi32.dll to release some internally cached handles +// that were made during calls to RegOpenkey and RegOpenKeyEx if it is called +// with a more restrictive token. Returns true if the flushing is succesful +// although this behavior is undocumented and there is no guarantee that in +// fact this will happen in future versions of windows. +bool FlushCachedRegHandles() { + return (FlushRegKey(HKEY_LOCAL_MACHINE) && + FlushRegKey(HKEY_CLASSES_ROOT) && + FlushRegKey(HKEY_USERS)); +} + +// Checks if we have handle entries pending and runs the closer. +bool CloseOpenHandles() { + if (sandbox::HandleCloserAgent::NeedsHandlesClosed()) { + sandbox::HandleCloserAgent handle_closer; + + handle_closer.InitializeHandlesToClose(); + if (!handle_closer.CloseHandles()) + return false; + } + + return true; +} + +} // namespace + +namespace sandbox { + +SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level = + INTEGRITY_LEVEL_LAST; + +TargetServicesBase::TargetServicesBase() { +} + +ResultCode TargetServicesBase::Init() { + process_state_.SetInitCalled(); + return SBOX_ALL_OK; +} + +// Failure here is a breach of security so the process is terminated. +void TargetServicesBase::LowerToken() { + if (ERROR_SUCCESS != + SetProcessIntegrityLevel(g_shared_delayed_integrity_level)) + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_INTEGRITY); + process_state_.SetRevertedToSelf(); + // If the client code as called RegOpenKey, advapi32.dll has cached some + // handles. The following code gets rid of them. + if (!::RevertToSelf()) + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_DROPTOKEN); + if (!FlushCachedRegHandles()) + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES); + if (ERROR_SUCCESS != ::RegDisablePredefinedCache()) + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE); + if (!CloseOpenHandles()) + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES); +} + +ProcessState* TargetServicesBase::GetState() { + return &process_state_; +} + +TargetServicesBase* TargetServicesBase::GetInstance() { + static TargetServicesBase instance; + return &instance; +} + +// The broker services a 'test' IPC service with the IPC_PING_TAG tag. +bool TargetServicesBase::TestIPCPing(int version) { + void* memory = GetGlobalIPCMemory(); + if (NULL == memory) { + return false; + } + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + + if (1 == version) { + uint32 tick1 = ::GetTickCount(); + uint32 cookie = 717115; + ResultCode code = CrossCall(ipc, IPC_PING1_TAG, cookie, &answer); + + if (SBOX_ALL_OK != code) { + return false; + } + // We should get two extended returns values from the IPC, one is the + // tick count on the broker and the other is the cookie times two. + if ((answer.extended_count != 2)) { + return false; + } + // We test the first extended answer to be within the bounds of the tick + // count only if there was no tick count wraparound. + uint32 tick2 = ::GetTickCount(); + if (tick2 >= tick1) { + if ((answer.extended[0].unsigned_int < tick1) || + (answer.extended[0].unsigned_int > tick2)) { + return false; + } + } + + if (answer.extended[1].unsigned_int != cookie * 2) { + return false; + } + } else if (2 == version) { + uint32 cookie = 717111; + InOutCountedBuffer counted_buffer(&cookie, sizeof(cookie)); + ResultCode code = CrossCall(ipc, IPC_PING2_TAG, counted_buffer, &answer); + + if (SBOX_ALL_OK != code) { + return false; + } + if (cookie != 717111 * 3) { + return false; + } + } else { + return false; + } + return true; +} + +bool ProcessState::IsKernel32Loaded() { + return process_state_ != 0; +} + +bool ProcessState::InitCalled() { + return process_state_ > 1; +} + +bool ProcessState::RevertedToSelf() { + return process_state_ > 2; +} + +void ProcessState::SetKernel32Loaded() { + if (!process_state_) + process_state_ = 1; +} + +void ProcessState::SetInitCalled() { + if (process_state_ < 2) + process_state_ = 2; +} + +void ProcessState::SetRevertedToSelf() { + if (process_state_ < 3) + process_state_ = 3; +} + +ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle, + DWORD target_process_id, + HANDLE* target_handle, + DWORD desired_access, + DWORD options) { + return sandbox::DuplicateHandleProxy(source_handle, target_process_id, + target_handle, desired_access, options); +} + +} // namespace sandbox diff --git a/sandbox/win/src/target_services.h b/sandbox/win/src/target_services.h new file mode 100644 index 0000000..c4bf4f6 --- /dev/null +++ b/sandbox/win/src/target_services.h @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_TARGET_SERVICES_H__ +#define SANDBOX_SRC_TARGET_SERVICES_H__ + +#include "base/basictypes.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/win_utils.h" + +namespace sandbox { + +class ProcessState { + public: + ProcessState() : process_state_(0) {} + + // Returns true if kernel32.dll has been loaded. + bool IsKernel32Loaded(); + + // Returns true if main has been called. + bool InitCalled(); + + // Returns true if LowerToken has been called. + bool RevertedToSelf(); + + // Set the current state. + void SetKernel32Loaded(); + void SetInitCalled(); + void SetRevertedToSelf(); + + public: + int process_state_; + DISALLOW_COPY_AND_ASSIGN(ProcessState); +}; + +// This class is an implementation of the TargetServices. +// Look in the documentation of sandbox::TargetServices for more info. +// Do NOT add a destructor to this class without changing the implementation of +// the factory method. +class TargetServicesBase : public TargetServices { + public: + TargetServicesBase(); + + // Public interface of TargetServices. + virtual ResultCode Init(); + virtual void LowerToken(); + virtual ProcessState* GetState(); + virtual ResultCode DuplicateHandle(HANDLE source_handle, + DWORD target_process_id, + HANDLE* target_handle, + DWORD desired_access, + DWORD options); + + // Factory method. + static TargetServicesBase* GetInstance(); + + // Sends a simple IPC Message that has a well-known answer. Returns true + // if the IPC was successful and false otherwise. There are 2 versions of + // this test: 1 and 2. The first one send a simple message while the + // second one send a message with an in/out param. + bool TestIPCPing(int version); + + private: + ProcessState process_state_; + DISALLOW_COPY_AND_ASSIGN(TargetServicesBase); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_TARGET_SERVICES_H__ diff --git a/sandbox/win/src/threadpool_unittest.cc b/sandbox/win/src/threadpool_unittest.cc new file mode 100644 index 0000000..2c85b57 --- /dev/null +++ b/sandbox/win/src/threadpool_unittest.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/win2k_threadpool.h" +#include "testing/gtest/include/gtest/gtest.h" + +void __stdcall EmptyCallBack(void*, unsigned char) { +} + +void __stdcall TestCallBack(void* context, unsigned char) { + HANDLE event = reinterpret_cast(context); + ::SetEvent(event); +} + +namespace sandbox { + +// Test that register and unregister work, part 1. +TEST(IPCTest, ThreadPoolRegisterTest1) { + Win2kThreadPool thread_pool; + + EXPECT_EQ(0, thread_pool.OutstandingWaits()); + + HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL); + HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL); + + uint32 context = 0; + EXPECT_FALSE(thread_pool.RegisterWait(0, event1, EmptyCallBack, &context)); + EXPECT_EQ(0, thread_pool.OutstandingWaits()); + + EXPECT_TRUE(thread_pool.RegisterWait(this, event1, EmptyCallBack, &context)); + EXPECT_EQ(1, thread_pool.OutstandingWaits()); + EXPECT_TRUE(thread_pool.RegisterWait(this, event2, EmptyCallBack, &context)); + EXPECT_EQ(2, thread_pool.OutstandingWaits()); + + EXPECT_TRUE(thread_pool.UnRegisterWaits(this)); + EXPECT_EQ(0, thread_pool.OutstandingWaits()); + + EXPECT_EQ(TRUE, ::CloseHandle(event1)); + EXPECT_EQ(TRUE, ::CloseHandle(event2)); +} + +// Test that register and unregister work, part 2. +TEST(IPCTest, ThreadPoolRegisterTest2) { + Win2kThreadPool thread_pool; + + HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL); + HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL); + + uint32 context = 0; + uint32 c1 = 0; + uint32 c2 = 0; + + EXPECT_TRUE(thread_pool.RegisterWait(&c1, event1, EmptyCallBack, &context)); + EXPECT_EQ(1, thread_pool.OutstandingWaits()); + EXPECT_TRUE(thread_pool.RegisterWait(&c2, event2, EmptyCallBack, &context)); + EXPECT_EQ(2, thread_pool.OutstandingWaits()); + + EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2)); + EXPECT_EQ(1, thread_pool.OutstandingWaits()); + EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2)); + EXPECT_EQ(1, thread_pool.OutstandingWaits()); + + EXPECT_TRUE(thread_pool.UnRegisterWaits(&c1)); + EXPECT_EQ(0, thread_pool.OutstandingWaits()); + + EXPECT_EQ(TRUE, ::CloseHandle(event1)); + EXPECT_EQ(TRUE, ::CloseHandle(event2)); +} + +// Test that the thread pool has at least a thread that services an event. +// Test that when the event is un-registered is no longer serviced. +TEST(IPCTest, ThreadPoolSignalAndWaitTest) { + Win2kThreadPool thread_pool; + + // The events are auto reset and start not signaled. + HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL); + HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL); + + EXPECT_TRUE(thread_pool.RegisterWait(this, event1, TestCallBack, event2)); + + EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, FALSE)); + EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, FALSE)); + + EXPECT_TRUE(thread_pool.UnRegisterWaits(this)); + EXPECT_EQ(0, thread_pool.OutstandingWaits()); + + EXPECT_EQ(WAIT_TIMEOUT, ::SignalObjectAndWait(event1, event2, 1000, FALSE)); + + EXPECT_EQ(TRUE, ::CloseHandle(event1)); + EXPECT_EQ(TRUE, ::CloseHandle(event2)); +} + +} // namespace sandbox diff --git a/sandbox/win/src/unload_dll_test.cc b/sandbox/win/src/unload_dll_test.cc new file mode 100644 index 0000000..2bce1b8 --- /dev/null +++ b/sandbox/win/src/unload_dll_test.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/win/scoped_handle.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/target_services.h" +#include "sandbox/tests/common/controller.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +// Loads and or unloads a DLL passed in the second parameter of argv. +// The first parameter of argv is 'L' = load, 'U' = unload or 'B' for both. +SBOX_TESTS_COMMAND int UseOneDLL(int argc, wchar_t **argv) { + if (argc != 2) + return SBOX_TEST_FAILED_TO_RUN_TEST; + int rv = SBOX_TEST_FAILED_TO_RUN_TEST; + + wchar_t option = (argv[0])[0]; + if ((option == L'L') || (option == L'B')) { + HMODULE module1 = ::LoadLibraryW(argv[1]); + rv = (module1 == NULL) ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED; + } + + if ((option == L'U') || (option == L'B')) { + HMODULE module2 = ::GetModuleHandleW(argv[1]); + rv = ::FreeLibrary(module2) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; + } + return rv; +} + +// Opens an event passed as the first parameter of argv. +SBOX_TESTS_COMMAND int SimpleOpenEvent(int argc, wchar_t **argv) { + if (argc != 1) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + base::win::ScopedHandle event_open(::OpenEvent(SYNCHRONIZE, FALSE, argv[0])); + return event_open.Get() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; +} + +// Flaky on windows, see http://crbug.com/80569. +TEST(UnloadDllTest, DISABLED_BaselineAvicapDll) { + TestRunner runner; + runner.SetTestState(BEFORE_REVERT); + runner.SetTimeout(2000); + // Add a sync rule, because that ensures that the interception agent has + // more than one item in its internal table. + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_ANY, L"t0001")); + + // Note for the puzzled: avicap32.dll is a 64-bit dll in 64-bit versions of + // windows so this test and the others just work. + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL L avicap32.dll")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL B avicap32.dll")); +} + +// Flaky on windows, see http://crbug.com/80569. +TEST(UnloadDllTest, DISABLED_UnloadAviCapDllNoPatching) { + TestRunner runner; + runner.SetTestState(BEFORE_REVERT); + runner.SetTimeout(2000); + sandbox::TargetPolicy* policy = runner.GetPolicy(); + policy->AddDllToUnload(L"avicap32.dll"); + EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll")); + EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL B avicap32.dll")); +} + +// Flaky: http://crbug.com/38404 +TEST(UnloadDllTest, DISABLED_UnloadAviCapDllWithPatching) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(BEFORE_REVERT); + sandbox::TargetPolicy* policy = runner.GetPolicy(); + policy->AddDllToUnload(L"avicap32.dll"); + + base::win::ScopedHandle handle1(::CreateEvent( + NULL, FALSE, FALSE, L"tst0001")); + + // Add a couple of rules that ensures that the interception agent add EAT + // patching on the client which makes sure that the unload dll record does + // not interact badly with them. + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_ANY, + L"HKEY_LOCAL_MACHINE\\Software\\Microsoft")); + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_ANY, L"tst0001")); + + EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll")); + + runner.SetTestState(AFTER_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"SimpleOpenEvent tst0001")); +} + +} // namespace sandbox diff --git a/sandbox/win/src/win2k_threadpool.cc b/sandbox/win/src/win2k_threadpool.cc new file mode 100644 index 0000000..fe2473e --- /dev/null +++ b/sandbox/win/src/win2k_threadpool.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/win2k_threadpool.h" + +#include "sandbox/src/win_utils.h" + +namespace sandbox { + +bool Win2kThreadPool::RegisterWait(const void* cookie, HANDLE waitable_object, + CrossCallIPCCallback callback, + void* context) { + if (0 == cookie) { + return false; + } + HANDLE pool_object = NULL; + // create a wait for a kernel object, with no timeout + if (!::RegisterWaitForSingleObject(&pool_object, waitable_object, callback, + context, INFINITE, WT_EXECUTEDEFAULT)) { + return false; + } + PoolObject pool_obj = {cookie, pool_object}; + AutoLock lock(&lock_); + pool_objects_.push_back(pool_obj); + return true; +} + +bool Win2kThreadPool::UnRegisterWaits(void* cookie) { + if (0 == cookie) { + return false; + } + AutoLock lock(&lock_); + bool success = true; + PoolObjects::iterator it = pool_objects_.begin(); + while (it != pool_objects_.end()) { + if (it->cookie == cookie) { + HANDLE wait = it->wait; + it = pool_objects_.erase(it); + success &= (::UnregisterWaitEx(wait, INVALID_HANDLE_VALUE) != 0); + } else { + ++it; + } + } + return success; +} + +size_t Win2kThreadPool::OutstandingWaits() { + AutoLock lock(&lock_); + return pool_objects_.size(); +} + +Win2kThreadPool::~Win2kThreadPool() { + // Here we used to unregister all the pool wait handles. Now, following the + // rest of the code we avoid lengthy or blocking calls given that the process + // is being torn down. + ::DeleteCriticalSection(&lock_); +} + +} // namespace sandbox diff --git a/sandbox/win/src/win2k_threadpool.h b/sandbox/win/src/win2k_threadpool.h new file mode 100644 index 0000000..8593fa3 --- /dev/null +++ b/sandbox/win/src/win2k_threadpool.h @@ -0,0 +1,58 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_WIN2K_THREADPOOL_H_ +#define SANDBOX_SRC_WIN2K_THREADPOOL_H_ + +#include +#include +#include "sandbox/src/crosscall_server.h" + +namespace sandbox { + +// Win2kThreadPool a simple implementation of a thread provider as required +// for the sandbox IPC subsystem. See sandbox\crosscall_server.h for the details +// and requirements of this interface. +// +// Implementing the thread provider as a thread pool is desirable in the case +// of shared memory IPC because it can generate a large number of waitable +// events: as many as channels. A thread pool does not create a thread per +// event, instead maintains a few idle threads but can create more if the need +// arises. +// +// This implementation simply thunks to the nice thread pool API of win2k. +class Win2kThreadPool : public ThreadProvider { + public: + Win2kThreadPool() { + ::InitializeCriticalSection(&lock_); + } + virtual ~Win2kThreadPool(); + + virtual bool RegisterWait(const void* cookie, HANDLE waitable_object, + CrossCallIPCCallback callback, + void* context); + + virtual bool UnRegisterWaits(void* cookie); + + // Returns the total number of wait objects associated with + // the thread pool. + size_t OutstandingWaits(); + + private: + // record to keep track of a wait and its associated cookie. + struct PoolObject { + const void* cookie; + HANDLE wait; + }; + // The list of pool wait objects. + typedef std::list PoolObjects; + PoolObjects pool_objects_; + // This lock protects the list of pool wait objects. + CRITICAL_SECTION lock_; + DISALLOW_COPY_AND_ASSIGN(Win2kThreadPool); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_WIN2K_THREADPOOL_H_ diff --git a/sandbox/win/src/win_utils.cc b/sandbox/win/src/win_utils.cc new file mode 100644 index 0000000..8a43d97 --- /dev/null +++ b/sandbox/win/src/win_utils.cc @@ -0,0 +1,323 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/win_utils.h" + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "sandbox/src/internal_types.h" +#include "sandbox/src/nt_internals.h" + +namespace { + +// Holds the information about a known registry key. +struct KnownReservedKey { + const wchar_t* name; + HKEY key; +}; + +// Contains all the known registry key by name and by handle. +const KnownReservedKey kKnownKey[] = { + { L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT }, + { L"HKEY_CURRENT_USER", HKEY_CURRENT_USER }, + { L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE}, + { L"HKEY_USERS", HKEY_USERS}, + { L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA}, + { L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT}, + { L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT}, + { L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG}, + { L"HKEY_DYN_DATA", HKEY_DYN_DATA} +}; + +// Returns true if the provided path points to a pipe. +bool IsPipe(const std::wstring& path) { + size_t start = 0; + if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix)) + start = sandbox::kNTPrefixLen; + + const wchar_t kPipe[] = L"pipe\\"; + return (0 == path.compare(start, arraysize(kPipe) - 1, kPipe)); +} + +} // namespace + +namespace sandbox { + +HKEY GetReservedKeyFromName(const std::wstring& name) { + for (size_t i = 0; i < arraysize(kKnownKey); ++i) { + if (name == kKnownKey[i].name) + return kKnownKey[i].key; + } + + return NULL; +} + +bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name) { + for (size_t i = 0; i < arraysize(kKnownKey); ++i) { + if (name.find(kKnownKey[i].name) == 0) { + HKEY key; + DWORD disposition; + if (ERROR_SUCCESS != ::RegCreateKeyEx(kKnownKey[i].key, L"", 0, NULL, 0, + MAXIMUM_ALLOWED, NULL, &key, + &disposition)) + return false; + + bool result = GetPathFromHandle(key, resolved_name); + ::RegCloseKey(key); + + if (!result) + return false; + + *resolved_name += name.substr(wcslen(kKnownKey[i].name)); + return true; + } + } + + return false; +} + +DWORD IsReparsePoint(const std::wstring& full_path, bool* result) { + std::wstring path = full_path; + + // Remove the nt prefix. + if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) + path = path.substr(kNTPrefixLen); + + // Check if it's a pipe. We can't query the attributes of a pipe. + if (IsPipe(path)) { + *result = FALSE; + return ERROR_SUCCESS; + } + + std::wstring::size_type last_pos = std::wstring::npos; + + do { + path = path.substr(0, last_pos); + + DWORD attributes = ::GetFileAttributes(path.c_str()); + if (INVALID_FILE_ATTRIBUTES == attributes) { + DWORD error = ::GetLastError(); + if (error != ERROR_FILE_NOT_FOUND && + error != ERROR_PATH_NOT_FOUND && + error != ERROR_INVALID_NAME) { + // Unexpected error. + NOTREACHED(); + return error; + } + } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) { + // This is a reparse point. + *result = true; + return ERROR_SUCCESS; + } + + last_pos = path.rfind(L'\\'); + } while (last_pos != std::wstring::npos); + + *result = false; + return ERROR_SUCCESS; +} + +// We get a |full_path| of the form \??\c:\some\foo\bar, and the name that +// we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar. +bool SameObject(HANDLE handle, const wchar_t* full_path) { + std::wstring path(full_path); + DCHECK(!path.empty()); + + // Check if it's a pipe. + if (IsPipe(path)) + return true; + + std::wstring actual_path; + if (!GetPathFromHandle(handle, &actual_path)) + return false; + + // This may end with a backslash. + const wchar_t kBackslash = '\\'; + if (path[path.length() - 1] == kBackslash) + path = path.substr(0, path.length() - 1); + + // Perfect match (case-insesitive check). + if (0 == _wcsicmp(actual_path.c_str(), path.c_str())) + return true; + + // Look for the drive letter. + size_t colon_pos = path.find(L':'); + if (colon_pos == 0 || colon_pos == std::wstring::npos) + return false; + + // Only one character for the drive. + if (colon_pos > 1 && path[colon_pos - 2] != kBackslash) + return false; + + // We only need 3 chars, but let's alloc a buffer for four. + wchar_t drive[4] = {0}; + wchar_t vol_name[MAX_PATH]; + memcpy(drive, &path[colon_pos - 1], 2 * sizeof(*drive)); + + // We'll get a double null terminated string. + DWORD vol_length = ::QueryDosDeviceW(drive, vol_name, MAX_PATH); + if (vol_length < 2 || vol_length == MAX_PATH) + return false; + + // Ignore the nulls at the end. + vol_length = static_cast(wcslen(vol_name)); + + // The two paths should be the same length. + if (vol_length + path.size() - (colon_pos + 1) != actual_path.size()) + return false; + + // Check up to the drive letter. + if (0 != _wcsnicmp(actual_path.c_str(), vol_name, vol_length)) + return false; + + // Check the path after the drive letter. + if (0 != _wcsicmp(&actual_path[vol_length], &path[colon_pos + 1])) + return false; + + return true; +} + +bool ConvertToLongPath(const std::wstring& short_path, + std::wstring* long_path) { + // Check if the path is a NT path. + bool is_nt_path = false; + std::wstring path = short_path; + if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) { + path = path.substr(kNTPrefixLen); + is_nt_path = true; + } + + DWORD size = MAX_PATH; + scoped_array long_path_buf(new wchar_t[size]); + + DWORD return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(), + size); + while (return_value >= size) { + size *= 2; + long_path_buf.reset(new wchar_t[size]); + return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(), size); + } + + DWORD last_error = ::GetLastError(); + if (0 == return_value && (ERROR_FILE_NOT_FOUND == last_error || + ERROR_PATH_NOT_FOUND == last_error || + ERROR_INVALID_NAME == last_error)) { + // The file does not exist, but maybe a sub path needs to be expanded. + std::wstring::size_type last_slash = path.rfind(L'\\'); + if (std::wstring::npos == last_slash) + return false; + + std::wstring begin = path.substr(0, last_slash); + std::wstring end = path.substr(last_slash); + if (!ConvertToLongPath(begin, &begin)) + return false; + + // Ok, it worked. Let's reset the return value. + path = begin + end; + return_value = 1; + } else if (0 != return_value) { + path = long_path_buf.get(); + } + + if (return_value != 0) { + if (is_nt_path) { + *long_path = kNTPrefix; + *long_path += path; + } else { + *long_path = path; + } + + return true; + } + + return false; +} + +bool GetPathFromHandle(HANDLE handle, std::wstring* path) { + NtQueryObjectFunction NtQueryObject = NULL; + ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject); + + OBJECT_NAME_INFORMATION initial_buffer; + OBJECT_NAME_INFORMATION* name = &initial_buffer; + ULONG size = sizeof(initial_buffer); + // Query the name information a first time to get the size of the name. + NTSTATUS status = NtQueryObject(handle, ObjectNameInformation, name, size, + &size); + + scoped_ptr name_ptr; + if (size) { + name = reinterpret_cast(new BYTE[size]); + name_ptr.reset(name); + + // Query the name information a second time to get the name of the + // object referenced by the handle. + status = NtQueryObject(handle, ObjectNameInformation, name, size, &size); + } + + if (STATUS_SUCCESS != status) + return false; + + path->assign(name->ObjectName.Buffer, name->ObjectName.Length / + sizeof(name->ObjectName.Buffer[0])); + return true; +} + +bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path) { + HANDLE file = ::CreateFileW(path.c_str(), 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (file == INVALID_HANDLE_VALUE) + return false; + bool rv = GetPathFromHandle(file, nt_path); + ::CloseHandle(file); + return rv; +} + +bool WriteProtectedChildMemory(HANDLE child_process, void* address, + const void* buffer, size_t length) { + // First, remove the protections. + DWORD old_protection; + if (!::VirtualProtectEx(child_process, address, length, + PAGE_WRITECOPY, &old_protection)) + return false; + + SIZE_T written; + bool ok = ::WriteProcessMemory(child_process, address, buffer, length, + &written) && (length == written); + + // Always attempt to restore the original protection. + if (!::VirtualProtectEx(child_process, address, length, + old_protection, &old_protection)) + return false; + + return ok; +} + +}; // namespace sandbox + +// TODO(jschuh): http://crbug.com/11789 +// I'm guessing we have a race where some "security" software is messing +// with ntdll/imports underneath us. So, we retry a few times, and in the +// worst case we sleep briefly before a few more attempts. (Normally sleeping +// would be very bad, but it's better than crashing in this case.) +void ResolveNTFunctionPtr(const char* name, void* ptr) { + const int max_tries = 5; + const int sleep_threshold = 2; + + static HMODULE ntdll = ::GetModuleHandle(sandbox::kNtdllName); + + FARPROC* function_ptr = reinterpret_cast(ptr); + *function_ptr = ::GetProcAddress(ntdll, name); + + for (int tries = 1; !(*function_ptr) && tries < max_tries; ++tries) { + if (tries >= sleep_threshold) + ::Sleep(1); + ntdll = ::GetModuleHandle(sandbox::kNtdllName); + *function_ptr = ::GetProcAddress(ntdll, name); + } + + CHECK(*function_ptr); +} diff --git a/sandbox/win/src/win_utils.h b/sandbox/win/src/win_utils.h new file mode 100644 index 0000000..a80bb81 --- /dev/null +++ b/sandbox/win/src/win_utils.h @@ -0,0 +1,110 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_WIN_UTILS_H_ +#define SANDBOX_SRC_WIN_UTILS_H_ + +#include +#include +#include "base/basictypes.h" + +namespace sandbox { + +// Prefix for path used by NT calls. +const wchar_t kNTPrefix[] = L"\\??\\"; +const size_t kNTPrefixLen = arraysize(kNTPrefix) - 1; + +const wchar_t kNTObjManPrefix[] = L"\\Device\\"; +const size_t kNTObjManPrefixLen = arraysize(kNTObjManPrefix) - 1; + +// Automatically acquires and releases a lock when the object is +// is destroyed. +class AutoLock { + public: + // Acquires the lock. + explicit AutoLock(CRITICAL_SECTION *lock) : lock_(lock) { + ::EnterCriticalSection(lock); + }; + + // Releases the lock; + ~AutoLock() { + ::LeaveCriticalSection(lock_); + }; + + private: + CRITICAL_SECTION *lock_; + DISALLOW_IMPLICIT_CONSTRUCTORS(AutoLock); +}; + +// Basic implementation of a singleton which calls the destructor +// when the exe is shutting down or the DLL is being unloaded. +template +class SingletonBase { + public: + static Derived* GetInstance() { + static Derived* instance = NULL; + if (NULL == instance) { + instance = new Derived(); + // Microsoft CRT extension. In an exe this this called after + // winmain returns, in a dll is called in DLL_PROCESS_DETACH + _onexit(OnExit); + } + return instance; + } + + private: + // this is the function that gets called by the CRT when the + // process is shutting down. + static int __cdecl OnExit() { + delete GetInstance(); + return 0; + } +}; + +// Convert a short path (C:\path~1 or \\??\\c:\path~1) to the long version of +// the path. If the path is not a valid filesystem path, the function returns +// false and the output parameter is not modified. +bool ConvertToLongPath(const std::wstring& short_path, std::wstring* long_path); + +// Sets result to true if the path contains a reparse point. The return value +// is ERROR_SUCCESS when the function succeeds or the appropriate error code +// when the function fails. +// This function is not smart. It looks for each element in the path and +// returns true if any of them is a reparse point. +DWORD IsReparsePoint(const std::wstring& full_path, bool* result); + +// Returns true if the handle corresponds to the object pointed by this path. +bool SameObject(HANDLE handle, const wchar_t* full_path); + +// Resolves a handle to an nt path. Returns true if the handle can be resolved. +bool GetPathFromHandle(HANDLE handle, std::wstring* path); + +// Resolves a win32 path to an nt path using GetPathFromHandle. The path must +// exist. Returs true if the translation was succesful. +bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path); + +// Translates a reserved key name to its handle. +// For example "HKEY_LOCAL_MACHINE" returns HKEY_LOCAL_MACHINE. +// Returns NULL if the name does not represent any reserved key name. +HKEY GetReservedKeyFromName(const std::wstring& name); + +// Resolves a user-readable registry path to a system-readable registry path. +// For example, HKEY_LOCAL_MACHINE\\Software\\microsoft is translated to +// \\registry\\machine\\software\\microsoft. Returns false if the path +// cannot be resolved. +bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name); + +// Writes |length| bytes from the provided |buffer| into the address space of +// |child_process|, at the specified |address|, preserving the original write +// protection attributes. Returns true on success. +bool WriteProtectedChildMemory(HANDLE child_process, void* address, + const void* buffer, size_t length); + +} // namespace sandbox + +// Resolves a function name in NTDLL to a function pointer. The second parameter +// is a pointer to the function pointer. +void ResolveNTFunctionPtr(const char* name, void* ptr); + +#endif // SANDBOX_SRC_WIN_UTILS_H_ diff --git a/sandbox/win/src/win_utils_unittest.cc b/sandbox/win/src/win_utils_unittest.cc new file mode 100644 index 0000000..0f445ef --- /dev/null +++ b/sandbox/win/src/win_utils_unittest.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/win/scoped_handle.h" +#include "sandbox/src/win_utils.h" +#include "sandbox/tests/common/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(WinUtils, IsReparsePoint) { + using sandbox::IsReparsePoint; + + // Create a temp file because we need write access to it. + wchar_t temp_directory[MAX_PATH]; + wchar_t my_folder[MAX_PATH]; + ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u); + + // Delete the file and create a directory instead. + ASSERT_TRUE(::DeleteFile(my_folder)); + ASSERT_TRUE(::CreateDirectory(my_folder, NULL)); + + bool result = true; + EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(my_folder, &result)); + EXPECT_FALSE(result); + + // We have to fix Bug 32224 to pass this test. + std::wstring not_found = std::wstring(my_folder) + L"\\foo\\bar"; + // EXPECT_EQ(ERROR_PATH_NOT_FOUND, IsReparsePoint(not_found, &result)); + + std::wstring new_file = std::wstring(my_folder) + L"\\foo"; + EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result)); + EXPECT_FALSE(result); + + // Replace the directory with a reparse point to %temp%. + HANDLE dir = ::CreateFile(my_folder, FILE_ALL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + EXPECT_NE(INVALID_HANDLE_VALUE, dir); + + std::wstring temp_dir_nt = std::wstring(L"\\??\\") + temp_directory; + EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); + + EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result)); + EXPECT_TRUE(result); + + EXPECT_TRUE(DeleteReparsePoint(dir)); + EXPECT_TRUE(::CloseHandle(dir)); + EXPECT_TRUE(::RemoveDirectory(my_folder)); +} + +TEST(WinUtils, SameObject) { + using sandbox::SameObject; + + // Create a temp file because we need write access to it. + wchar_t temp_directory[MAX_PATH]; + wchar_t my_folder[MAX_PATH]; + ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u); + + // Delete the file and create a directory instead. + ASSERT_TRUE(::DeleteFile(my_folder)); + ASSERT_TRUE(::CreateDirectory(my_folder, NULL)); + + std::wstring folder(my_folder); + std::wstring file_name = folder + L"\\foo.txt"; + const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE; + base::win::ScopedHandle file(CreateFile( + file_name.c_str(), GENERIC_WRITE, kSharing, NULL, CREATE_ALWAYS, + FILE_FLAG_DELETE_ON_CLOSE, NULL)); + + EXPECT_TRUE(file.IsValid()); + std::wstring file_name_nt1 = std::wstring(L"\\??\\") + file_name; + std::wstring file_name_nt2 = std::wstring(L"\\??\\") + folder + L"\\FOO.txT"; + EXPECT_TRUE(SameObject(file.Get(), file_name_nt1.c_str())); + EXPECT_TRUE(SameObject(file.Get(), file_name_nt2.c_str())); + + file.Close(); + EXPECT_TRUE(::RemoveDirectory(my_folder)); +} diff --git a/sandbox/win/src/window.cc b/sandbox/win/src/window.cc new file mode 100644 index 0000000..507ae57 --- /dev/null +++ b/sandbox/win/src/window.cc @@ -0,0 +1,142 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/window.h" + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" + +namespace { + +// Gets the security attributes of a window object referenced by |handle|. The +// lpSecurityDescriptor member of the SECURITY_ATTRIBUTES parameter returned +// must be freed using LocalFree by the caller. +bool GetSecurityAttributes(HANDLE handle, SECURITY_ATTRIBUTES* attributes) { + attributes->bInheritHandle = FALSE; + attributes->nLength = sizeof(SECURITY_ATTRIBUTES); + + PACL dacl = NULL; + DWORD result = ::GetSecurityInfo(handle, SE_WINDOW_OBJECT, + DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, + NULL, &attributes->lpSecurityDescriptor); + if (ERROR_SUCCESS == result) + return true; + + return false; +} + +} + +namespace sandbox { + +ResultCode CreateAltWindowStation(HWINSTA* winsta) { + // Get the security attributes from the current window station; we will + // use this as the base security attributes for the new window station. + SECURITY_ATTRIBUTES attributes = {0}; + if (!GetSecurityAttributes(::GetProcessWindowStation(), &attributes)) { + return SBOX_ERROR_CANNOT_CREATE_WINSTATION; + } + + // Create the window station using NULL for the name to ask the os to + // generate it. + // TODO(nsylvain): don't ask for WINSTA_ALL_ACCESS if we don't need to. + *winsta = ::CreateWindowStationW(NULL, 0, WINSTA_ALL_ACCESS, &attributes); + LocalFree(attributes.lpSecurityDescriptor); + + if (*winsta) + return SBOX_ALL_OK; + + return SBOX_ERROR_CANNOT_CREATE_WINSTATION; +} + +ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) { + std::wstring desktop_name = L"sbox_alternate_desktop_"; + + // Append the current PID to the desktop name. + wchar_t buffer[16]; + _snwprintf_s(buffer, sizeof(buffer) / sizeof(wchar_t), L"0x%X", + ::GetCurrentProcessId()); + desktop_name += buffer; + + // Get the security attributes from the current desktop, we will use this as + // the base security attributes for the new desktop. + SECURITY_ATTRIBUTES attributes = {0}; + if (!GetSecurityAttributes(GetThreadDesktop(GetCurrentThreadId()), + &attributes)) { + return SBOX_ERROR_CANNOT_CREATE_DESKTOP; + } + + // Back up the current window station, in case we need to switch it. + HWINSTA current_winsta = ::GetProcessWindowStation(); + + if (winsta) { + // We need to switch to the alternate window station before creating the + // desktop. + if (!::SetProcessWindowStation(winsta)) { + ::LocalFree(attributes.lpSecurityDescriptor); + return SBOX_ERROR_CANNOT_CREATE_DESKTOP; + } + } + + // Create the destkop. + // TODO(nsylvain): don't ask for GENERIC_ALL if we don't need to. + *desktop = ::CreateDesktop(desktop_name.c_str(), NULL, NULL, 0, GENERIC_ALL, + &attributes); + ::LocalFree(attributes.lpSecurityDescriptor); + + if (winsta) { + // Revert to the right window station. + if (!::SetProcessWindowStation(current_winsta)) { + return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION; + } + } + + if (*desktop) + return SBOX_ALL_OK; + + return SBOX_ERROR_CANNOT_CREATE_DESKTOP; +} + +std::wstring GetWindowObjectName(HANDLE handle) { + // Get the size of the name. + DWORD size = 0; + ::GetUserObjectInformation(handle, UOI_NAME, NULL, 0, &size); + + if (!size) { + NOTREACHED(); + return std::wstring(); + } + + // Create the buffer that will hold the name. + scoped_array name_buffer(new wchar_t[size]); + + // Query the name of the object. + if (!::GetUserObjectInformation(handle, UOI_NAME, name_buffer.get(), size, + &size)) { + NOTREACHED(); + return std::wstring(); + } + + return std::wstring(name_buffer.get()); +} + +std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop) { + if (!desktop) { + NOTREACHED(); + return std::wstring(); + } + + std::wstring name; + if (winsta) { + name = GetWindowObjectName(winsta); + name += L'\\'; + } + + name += GetWindowObjectName(desktop); + return name; +} + +} // namespace sandbox diff --git a/sandbox/win/src/window.h b/sandbox/win/src/window.h new file mode 100644 index 0000000..f65b463 --- /dev/null +++ b/sandbox/win/src/window.h @@ -0,0 +1,39 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_SRC_WINDOW_H_ +#define SANDBOX_SRC_WINDOW_H_ + +#include +#include + +#include "sandbox/src/sandbox_types.h" + +namespace sandbox { + + // Creates a window station. The name is generated by the OS. The security + // descriptor is based on the security descriptor of the current window + // station. + ResultCode CreateAltWindowStation(HWINSTA* winsta); + + // Creates a desktop. The name is a static string followed by the pid of the + // current process. The security descriptor on the new desktop is based on the + // security descriptor of the desktop associated with the current thread. + // If a winsta is specified, the function will switch to it before creating + // the desktop. If the functions fails the switch back to the current winsta, + // the function will return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION. + ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop); + + // Returns the name of a desktop or a window station. + std::wstring GetWindowObjectName(HANDLE handle); + + // Returns the name of the desktop referenced by |desktop|. If a window + // station is specified, the name is prepended with the window station name, + // followed by a backslash. This name can be used as the lpDesktop parameter + // to CreateProcess. + std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop); + +} // namespace sandbox + +#endif // SANDBOX_SRC_WINDOW_H_ diff --git a/sandbox/win/tests/common/controller.cc b/sandbox/win/tests/common/controller.cc new file mode 100644 index 0000000..6c73c2d --- /dev/null +++ b/sandbox/win/tests/common/controller.cc @@ -0,0 +1,338 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/tests/common/controller.h" + +#include + +#include "base/process.h" +#include "base/process_util.h" +#include "base/sys_string_conversions.h" +#include "base/win/windows_version.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/sandbox_utils.h" + +namespace { + +static const int kDefaultTimeout = 3000; + +// Constructs a full path to a file inside the system32 folder. +std::wstring MakePathToSys32(const wchar_t* name, bool is_obj_man_path) { + wchar_t windows_path[MAX_PATH] = {0}; + if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH)) + return std::wstring(); + + std::wstring full_path(windows_path); + if (full_path.empty()) + return full_path; + + if (is_obj_man_path) + full_path.insert(0, L"\\??\\"); + + full_path += L"\\system32\\"; + full_path += name; + return full_path; +} + +// Constructs a full path to a file inside the syswow64 folder. +std::wstring MakePathToSysWow64(const wchar_t* name, bool is_obj_man_path) { + wchar_t windows_path[MAX_PATH] = {0}; + if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH)) + return std::wstring(); + + std::wstring full_path(windows_path); + if (full_path.empty()) + return full_path; + + if (is_obj_man_path) + full_path.insert(0, L"\\??\\"); + + full_path += L"\\SysWOW64\\"; + full_path += name; + return full_path; +} + +bool IsProcessRunning(HANDLE process) { + DWORD exit_code = 0; + if (::GetExitCodeProcess(process, &exit_code)) + return exit_code == STILL_ACTIVE; + return false; +} + +} // namespace + +namespace sandbox { + +std::wstring MakePathToSys(const wchar_t* name, bool is_obj_man_path) { + return (base::win::OSInfo::GetInstance()->wow64_status() == + base::win::OSInfo::WOW64_ENABLED) ? + MakePathToSysWow64(name, is_obj_man_path) : + MakePathToSys32(name, is_obj_man_path); +} + +BrokerServices* GetBroker() { + static BrokerServices* broker = SandboxFactory::GetBrokerServices(); + static bool is_initialized = false; + + if (!broker) { + return NULL; + } + + if (!is_initialized) { + if (SBOX_ALL_OK != broker->Init()) + return NULL; + + is_initialized = true; + } + + return broker; +} + +TestRunner::TestRunner(JobLevel job_level, TokenLevel startup_token, + TokenLevel main_token) + : is_init_(false), is_async_(false), no_sandbox_(false), + target_process_id_(0) { + Init(job_level, startup_token, main_token); +} + +TestRunner::TestRunner() + : is_init_(false), is_async_(false), no_sandbox_(false), + target_process_id_(0) { + Init(JOB_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN); +} + +void TestRunner::Init(JobLevel job_level, TokenLevel startup_token, + TokenLevel main_token) { + broker_ = NULL; + policy_ = NULL; + timeout_ = kDefaultTimeout; + state_ = AFTER_REVERT; + is_async_= false; + target_process_id_ = 0; + + broker_ = GetBroker(); + if (!broker_) + return; + + policy_ = broker_->CreatePolicy(); + if (!policy_) + return; + + policy_->SetJobLevel(job_level, 0); + policy_->SetTokenLevel(startup_token, main_token); + + is_init_ = true; +} + +TargetPolicy* TestRunner::GetPolicy() { + return policy_; +} + +TestRunner::~TestRunner() { + if (target_process_) + ::TerminateProcess(target_process_, 0); + + if (policy_) + policy_->Release(); +} + +bool TestRunner::AddRule(TargetPolicy::SubSystem subsystem, + TargetPolicy::Semantics semantics, + const wchar_t* pattern) { + if (!is_init_) + return false; + + return (SBOX_ALL_OK == policy_->AddRule(subsystem, semantics, pattern)); +} + +bool TestRunner::AddRuleSys32(TargetPolicy::Semantics semantics, + const wchar_t* pattern) { + if (!is_init_) + return false; + + std::wstring win32_path = MakePathToSys32(pattern, false); + if (win32_path.empty()) + return false; + + if (!AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str())) + return false; + + if (base::win::OSInfo::GetInstance()->wow64_status() != + base::win::OSInfo::WOW64_ENABLED) + return true; + + win32_path = MakePathToSysWow64(pattern, false); + if (win32_path.empty()) + return false; + + return AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str()); +} + +bool TestRunner::AddFsRule(TargetPolicy::Semantics semantics, + const wchar_t* pattern) { + if (!is_init_) + return false; + + return AddRule(TargetPolicy::SUBSYS_FILES, semantics, pattern); +} + +int TestRunner::RunTest(const wchar_t* command) { + if (MAX_STATE > 10) + return SBOX_TEST_INVALID_PARAMETER; + + wchar_t state_number[2]; + state_number[0] = L'0' + state_; + state_number[1] = L'\0'; + std::wstring full_command(state_number); + full_command += L" "; + full_command += command; + + return InternalRunTest(full_command.c_str()); +} + +int TestRunner::InternalRunTest(const wchar_t* command) { + if (!is_init_) + return SBOX_TEST_FAILED_TO_RUN_TEST; + + // For simplicity TestRunner supports only one process per instance. + if (target_process_) { + if (IsProcessRunning(target_process_)) + return SBOX_TEST_FAILED_TO_RUN_TEST; + target_process_.Close(); + target_process_id_ = 0; + } + + // Get the path to the sandboxed process. + wchar_t prog_name[MAX_PATH]; + GetModuleFileNameW(NULL, prog_name, MAX_PATH); + + // Launch the sandboxed process. + ResultCode result = SBOX_ALL_OK; + PROCESS_INFORMATION target = {0}; + + std::wstring arguments(L"\""); + arguments += prog_name; + arguments += L"\" -child"; + arguments += no_sandbox_ ? L"-no-sandbox " : L" "; + arguments += command; + + if (no_sandbox_) { + STARTUPINFO startup_info = {sizeof(STARTUPINFO)}; + if (!::CreateProcessW(prog_name, &arguments[0], NULL, NULL, FALSE, 0, + NULL, NULL, &startup_info, &target)) { + return SBOX_ERROR_GENERIC; + } + broker_->AddTargetPeer(target.hProcess); + } else { + result = broker_->SpawnTarget(prog_name, arguments.c_str(), policy_, + &target); + } + + if (SBOX_ALL_OK != result) + return SBOX_TEST_FAILED_TO_RUN_TEST; + + ::ResumeThread(target.hThread); + + // For an asynchronous run we don't bother waiting. + if (is_async_) { + target_process_.Set(target.hProcess); + target_process_id_ = target.dwProcessId; + ::CloseHandle(target.hThread); + return SBOX_TEST_SUCCEEDED; + } + + if (::IsDebuggerPresent()) { + // Don't kill the target process on a time-out while we are debugging. + timeout_ = INFINITE; + } + + if (WAIT_TIMEOUT == ::WaitForSingleObject(target.hProcess, timeout_)) { + ::TerminateProcess(target.hProcess, SBOX_TEST_TIMED_OUT); + ::CloseHandle(target.hProcess); + ::CloseHandle(target.hThread); + return SBOX_TEST_TIMED_OUT; + } + + DWORD exit_code = SBOX_TEST_LAST_RESULT; + if (!::GetExitCodeProcess(target.hProcess, &exit_code)) { + ::CloseHandle(target.hProcess); + ::CloseHandle(target.hThread); + return SBOX_TEST_FAILED_TO_RUN_TEST; + } + + ::CloseHandle(target.hProcess); + ::CloseHandle(target.hThread); + + return exit_code; +} + +void TestRunner::SetTimeout(DWORD timeout_ms) { + timeout_ = timeout_ms; +} + +void TestRunner::SetTestState(SboxTestsState desired_state) { + state_ = desired_state; +} + +// This is the main procedure for the target (child) application. We'll find out +// the target test and call it. +// We expect the arguments to be: +// argv[1] = "-child" +// argv[2] = SboxTestsState when to run the command +// argv[3] = command to run +// argv[4...] = command arguments. +int DispatchCall(int argc, wchar_t **argv) { + if (argc < 4) + return SBOX_TEST_INVALID_PARAMETER; + + // We hard code two tests to avoid dispatch failures. + if (0 == _wcsicmp(argv[3], L"wait")) { + Sleep(INFINITE); + return SBOX_TEST_TIMED_OUT; + } + + if (0 == _wcsicmp(argv[3], L"ping")) + return SBOX_TEST_PING_OK; + + SboxTestsState state = static_cast(_wtoi(argv[2])); + if ((state <= MIN_STATE) || (state >= MAX_STATE)) + return SBOX_TEST_INVALID_PARAMETER; + + HMODULE module; + if (!GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(&DispatchCall), + &module)) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + std::string command_name = base::SysWideToMultiByte(argv[3], CP_UTF8); + CommandFunction command = reinterpret_cast( + ::GetProcAddress(module, command_name.c_str())); + if (!command) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + if (BEFORE_INIT == state) + return command(argc - 4, argv + 4); + else if (EVERY_STATE == state) + command(argc - 4, argv + 4); + + TargetServices* target = SandboxFactory::GetTargetServices(); + if (target) { + if (SBOX_ALL_OK != target->Init()) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + if (BEFORE_REVERT == state) + return command(argc - 4, argv + 4); + else if (EVERY_STATE == state) + command(argc - 4, argv + 4); + + target->LowerToken(); + } else if (0 != _wcsicmp(argv[1], L"-child-no-sandbox")) { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + + return command(argc - 4, argv + 4); +} + +} // namespace sandbox diff --git a/sandbox/win/tests/common/controller.h b/sandbox/win/tests/common/controller.h new file mode 100644 index 0000000..5c2a471 --- /dev/null +++ b/sandbox/win/tests/common/controller.h @@ -0,0 +1,145 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_TESTS_COMMON_CONTROLLER_H_ +#define SANDBOX_TESTS_COMMON_CONTROLLER_H__ + +#include +#include + +#include "base/win/scoped_handle.h" +#include "sandbox/src/sandbox.h" + +namespace sandbox { + +// See winerror.h for details. +#define SEVERITY_INFO_FLAGS 0x40000000 +#define SEVERITY_ERROR_FLAGS 0xC0000000 +#define CUSTOMER_CODE 0x20000000 +#define SBOX_TESTS_FACILITY 0x05B10000 + +// All the possible error codes returned by the child process in +// the sandbox. +enum SboxTestResult { + SBOX_TEST_FIRST_RESULT = CUSTOMER_CODE | SBOX_TESTS_FACILITY, + SBOX_TEST_SUCCEEDED, + SBOX_TEST_PING_OK, + SBOX_TEST_FIRST_INFO = SBOX_TEST_FIRST_RESULT | SEVERITY_INFO_FLAGS, + SBOX_TEST_DENIED, // Access was denied. + SBOX_TEST_NOT_FOUND, // The resource was not found. + SBOX_TEST_FIRST_ERROR = SBOX_TEST_FIRST_RESULT | SEVERITY_ERROR_FLAGS, + SBOX_TEST_INVALID_PARAMETER, + SBOX_TEST_FAILED_TO_RUN_TEST, + SBOX_TEST_FAILED_TO_EXECUTE_COMMAND, + SBOX_TEST_TIMED_OUT, + SBOX_TEST_FAILED, + SBOX_TEST_LAST_RESULT +}; + +inline bool IsSboxTestsResult(SboxTestResult result) { + unsigned int code = static_cast(result); + unsigned int first = static_cast(SBOX_TEST_FIRST_RESULT); + unsigned int last = static_cast(SBOX_TEST_LAST_RESULT); + return (code > first) && (code < last); +} + +enum SboxTestsState { + MIN_STATE = 1, + BEFORE_INIT, + BEFORE_REVERT, + AFTER_REVERT, + EVERY_STATE, + MAX_STATE +}; + +#define SBOX_TESTS_API __declspec(dllexport) +#define SBOX_TESTS_COMMAND extern "C" SBOX_TESTS_API + +extern "C" { +typedef int (*CommandFunction)(int argc, wchar_t **argv); +} + +// Class to facilitate the launch of a test inside the sandbox. +class TestRunner { + public: + TestRunner(JobLevel job_level, TokenLevel startup_token, + TokenLevel main_token); + + TestRunner(); + + ~TestRunner(); + + // Adds a rule to the policy. The parameters are the same as the AddRule + // function in the sandbox. + bool AddRule(TargetPolicy::SubSystem subsystem, + TargetPolicy::Semantics semantics, + const wchar_t* pattern); + + // Adds a filesystem rules with the path of a file in system32. The function + // appends "pattern" to "system32" and then call AddRule. Return true if the + // function succeeds. + bool AddRuleSys32(TargetPolicy::Semantics semantics, const wchar_t* pattern); + + // Adds a filesystem rules to the policy. Returns true if the functions + // succeeds. + bool AddFsRule(TargetPolicy::Semantics semantics, const wchar_t* pattern); + + // Starts a child process in the sandbox and ask it to run |command|. Returns + // a SboxTestResult. By default, the test runs AFTER_REVERT. + int RunTest(const wchar_t* command); + + // Sets the timeout value for the child to run the command and return. + void SetTimeout(DWORD timeout_ms); + + // Sets TestRunner to return without waiting for the process to exit. + void SetAsynchronous(bool is_async) { is_async_ = is_async; } + + // Sets TestRunner to return without waiting for the process to exit. + void SetUnsandboxed(bool is_no_sandbox) { no_sandbox_ = is_no_sandbox; } + + // Sets the desired state for the test to run. + void SetTestState(SboxTestsState desired_state); + + // Returns the pointers to the policy object. It can be used to modify + // the policy manually. + TargetPolicy* GetPolicy(); + + // Return the process handle for an asynchronous test. + HANDLE process() { return target_process_; } + + // Return the process ID for an asynchronous test. + DWORD process_id() { return target_process_id_; } + + private: + // Initializes the data in the object. Sets is_init_ to tree if the + // function succeeds. This is meant to be called from the constructor. + void Init(JobLevel job_level, TokenLevel startup_token, + TokenLevel main_token); + + // The actual runner. + int InternalRunTest(const wchar_t* command); + + BrokerServices* broker_; + TargetPolicy* policy_; + DWORD timeout_; + SboxTestsState state_; + bool is_init_; + bool is_async_; + bool no_sandbox_; + base::win::ScopedHandle target_process_; + DWORD target_process_id_; +}; + +// Returns the broker services. +BrokerServices* GetBroker(); + +// Constructs a full path to a file inside the system32 (or syswow64) folder. +std::wstring MakePathToSys(const wchar_t* name, bool is_obj_man_path); + +// Runs the given test on the target process. +int DispatchCall(int argc, wchar_t **argv); + +} // namespace sandbox + +#endif // SANDBOX_TESTS_COMMON_CONTROLLER_H_ diff --git a/sandbox/win/tests/common/test_utils.cc b/sandbox/win/tests/common/test_utils.cc new file mode 100644 index 0000000..929c322 --- /dev/null +++ b/sandbox/win/tests/common/test_utils.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/tests/common/test_utils.h" + +#include + +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + +// Sets a reparse point. |source| will now point to |target|. Returns true if +// the call succeeds, false otherwise. +bool SetReparsePoint(HANDLE source, const wchar_t* target) { + USHORT size_target = static_cast(wcslen(target)) * sizeof(target[0]); + + char buffer[2000] = {0}; + DWORD returned; + + REPARSE_DATA_BUFFER* data = reinterpret_cast(buffer); + + data->ReparseTag = 0xa0000003; + memcpy(data->MountPointReparseBuffer.PathBuffer, target, size_target + 2); + data->MountPointReparseBuffer.SubstituteNameLength = size_target; + data->MountPointReparseBuffer.PrintNameOffset = size_target + 2; + data->ReparseDataLength = size_target + 4 + 8; + + int data_size = data->ReparseDataLength + 8; + + if (!DeviceIoControl(source, FSCTL_SET_REPARSE_POINT, &buffer, data_size, + NULL, 0, &returned, NULL)) { + return false; + } + return true; +} + +// Delete the reparse point referenced by |source|. Returns true if the call +// succeeds, false otherwise. +bool DeleteReparsePoint(HANDLE source) { + DWORD returned; + REPARSE_DATA_BUFFER data = {0}; + data.ReparseTag = 0xa0000003; + if (!DeviceIoControl(source, FSCTL_DELETE_REPARSE_POINT, &data, 8, NULL, 0, + &returned, NULL)) { + return false; + } + + return true; +} diff --git a/sandbox/win/tests/common/test_utils.h b/sandbox/win/tests/common/test_utils.h new file mode 100644 index 0000000..9e17660 --- /dev/null +++ b/sandbox/win/tests/common/test_utils.h @@ -0,0 +1,19 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_TESTS_COMMON_TEST_UTILS_H_ +#define SANDBOX_TESTS_COMMON_TEST_UTILS_H_ + +#include + +// Sets a reparse point. |source| will now point to |target|. Returns true if +// the call succeeds, false otherwise. +bool SetReparsePoint(HANDLE source, const wchar_t* target); + +// Delete the reparse point referenced by |source|. Returns true if the call +// succeeds, false otherwise. +bool DeleteReparsePoint(HANDLE source); + +#endif // SANDBOX_TESTS_COMMON_TEST_UTILS_H_ + diff --git a/sandbox/win/tests/integration_tests/integration_tests.cc b/sandbox/win/tests/integration_tests/integration_tests.cc new file mode 100644 index 0000000..5096abb --- /dev/null +++ b/sandbox/win/tests/integration_tests/integration_tests.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/tests/common/controller.h" + +int wmain(int argc, wchar_t **argv) { + if (argc >= 2) { + if (0 == _wcsicmp(argv[1], L"-child") || + 0 == _wcsicmp(argv[1], L"-child-no-sandbox")) + // This instance is a child, not the test. + return sandbox::DispatchCall(argc, argv); + } + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/sandbox/win/tests/integration_tests/integration_tests_test.cc b/sandbox/win/tests/integration_tests/integration_tests_test.cc new file mode 100644 index 0000000..b610681 --- /dev/null +++ b/sandbox/win/tests/integration_tests/integration_tests_test.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Some tests for the framework itself. + +#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/target_services.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/tests/common/controller.h" + +namespace sandbox { + +// Returns the current process state. +SBOX_TESTS_COMMAND int IntegrationTestsTest_state(int argc, wchar_t **argv) { + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return BEFORE_INIT; + + if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) + return BEFORE_REVERT; + + return AFTER_REVERT; +} + +// Returns the current process state, keeping track of it. +SBOX_TESTS_COMMAND int IntegrationTestsTest_state2(int argc, wchar_t **argv) { + static SboxTestsState state = MIN_STATE; + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) { + if (MIN_STATE == state) + state = BEFORE_INIT; + return state; + } + + if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) { + if (BEFORE_INIT == state) + state = BEFORE_REVERT; + return state; + } + + if (BEFORE_REVERT == state) + state = AFTER_REVERT; + return state; +} + +// Returns the number of arguments +SBOX_TESTS_COMMAND int IntegrationTestsTest_args(int argc, wchar_t **argv) { + for (int i = 0; i < argc; i++) { + wchar_t argument[20]; + size_t argument_bytes = wcslen(argv[i]) * sizeof(wchar_t); + memcpy(argument, argv[i], __min(sizeof(argument), argument_bytes)); + } + + return argc; +} + +TEST(IntegrationTestsTest, CallsBeforeInit) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(BEFORE_INIT); + ASSERT_EQ(BEFORE_INIT, runner.RunTest(L"IntegrationTestsTest_state")); +} + +TEST(IntegrationTestsTest, CallsBeforeRevert) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(BEFORE_REVERT); + ASSERT_EQ(BEFORE_REVERT, runner.RunTest(L"IntegrationTestsTest_state")); +} + +TEST(IntegrationTestsTest, CallsAfterRevert) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(AFTER_REVERT); + ASSERT_EQ(AFTER_REVERT, runner.RunTest(L"IntegrationTestsTest_state")); +} + +TEST(IntegrationTestsTest, CallsEveryState) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(EVERY_STATE); + ASSERT_EQ(AFTER_REVERT, runner.RunTest(L"IntegrationTestsTest_state2")); +} + +TEST(IntegrationTestsTest, ForwardsArguments) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(BEFORE_INIT); + ASSERT_EQ(1, runner.RunTest(L"IntegrationTestsTest_args first")); + ASSERT_EQ(4, runner.RunTest(L"IntegrationTestsTest_args first second third " + L"fourth")); +} + +} // namespace sandbox diff --git a/sandbox/win/tests/integration_tests/sbox_integration_tests.vcproj b/sandbox/win/tests/integration_tests/sbox_integration_tests.vcproj new file mode 100644 index 0000000..53816e7 --- /dev/null +++ b/sandbox/win/tests/integration_tests/sbox_integration_tests.vcproj @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/win/tests/unit_tests/sbox_unittests.vcproj b/sandbox/win/tests/unit_tests/sbox_unittests.vcproj new file mode 100644 index 0000000..a2df792 --- /dev/null +++ b/sandbox/win/tests/unit_tests/sbox_unittests.vcproj @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/win/tests/unit_tests/unit_tests.cc b/sandbox/win/tests/unit_tests/unit_tests.cc new file mode 100644 index 0000000..a9623db --- /dev/null +++ b/sandbox/win/tests/unit_tests/unit_tests.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" + +int wmain(int argc, wchar_t **argv) { + if (argc >= 2) { + if (0 == _wcsicmp(argv[1], L"-child")) + // This instance is a child, not the test. + return 0; + } + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/sandbox/win/tests/validation_tests/commands.cc b/sandbox/win/tests/validation_tests/commands.cc new file mode 100644 index 0000000..d99451f --- /dev/null +++ b/sandbox/win/tests/validation_tests/commands.cc @@ -0,0 +1,262 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "sandbox/tests/validation_tests/commands.h" + +#include "sandbox/tests/common/controller.h" + +namespace { + +// Returns the HKEY corresponding to name. If there is no HKEY corresponding +// to the name it returns NULL. +HKEY GetHKEYFromString(const std::wstring &name) { + if (L"HKLM" == name) + return HKEY_LOCAL_MACHINE; + else if (L"HKCR" == name) + return HKEY_CLASSES_ROOT; + else if (L"HKCC" == name) + return HKEY_CURRENT_CONFIG; + else if (L"HKCU" == name) + return HKEY_CURRENT_USER; + else if (L"HKU" == name) + return HKEY_USERS; + + return NULL; +} + +// Modifies string to remove the leading and trailing quotes. +void trim_quote(std::wstring* string) { + std::wstring::size_type pos1 = string->find_first_not_of(L'"'); + std::wstring::size_type pos2 = string->find_last_not_of(L'"'); + + if (std::wstring::npos == pos1 || std::wstring::npos == pos2) + (*string) = L""; + else + (*string) = string->substr(pos1, pos2 + 1); +} + +int TestOpenFile(std::wstring path, bool for_write) { + wchar_t path_expanded[MAX_PATH + 1] = {0}; + DWORD size = ::ExpandEnvironmentStrings(path.c_str(), path_expanded, + MAX_PATH); + if (!size) + return sandbox::SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + HANDLE file; + file = ::CreateFile(path_expanded, + for_write ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, // No security attributes. + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); // No template. + + if (INVALID_HANDLE_VALUE != file) { + ::CloseHandle(file); + return sandbox::SBOX_TEST_SUCCEEDED; + } else { + if (ERROR_ACCESS_DENIED == ::GetLastError()) { + return sandbox::SBOX_TEST_DENIED; + } else { + return sandbox::SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + } +} + +} // namespace + +namespace sandbox { + +SBOX_TESTS_COMMAND int ValidWindow(int argc, wchar_t **argv) { + if (1 != argc) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + HWND window = reinterpret_cast(static_cast(_wtoi(argv[0]))); + + return TestValidWindow(window); +} + +int TestValidWindow(HWND window) { + if (::IsWindow(window)) + return SBOX_TEST_SUCCEEDED; + + return SBOX_TEST_DENIED; +} + +SBOX_TESTS_COMMAND int OpenProcessCmd(int argc, wchar_t **argv) { + if (2 != argc) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + DWORD process_id = _wtol(argv[0]); + DWORD access_mask = _wtol(argv[1]); + return TestOpenProcess(process_id, access_mask); +} + +int TestOpenProcess(DWORD process_id, DWORD access_mask) { + HANDLE process = ::OpenProcess(access_mask, + FALSE, // Do not inherit handle. + process_id); + if (NULL == process) { + if (ERROR_ACCESS_DENIED == ::GetLastError()) { + return SBOX_TEST_DENIED; + } else { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + } else { + ::CloseHandle(process); + return SBOX_TEST_SUCCEEDED; + } +} + +SBOX_TESTS_COMMAND int OpenThreadCmd(int argc, wchar_t **argv) { + if (1 != argc) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + DWORD thread_id = _wtoi(argv[0]); + return TestOpenThread(thread_id); +} + +int TestOpenThread(DWORD thread_id) { + + HANDLE thread = ::OpenThread(THREAD_QUERY_INFORMATION, + FALSE, // Do not inherit handles. + thread_id); + + if (NULL == thread) { + if (ERROR_ACCESS_DENIED == ::GetLastError()) { + return SBOX_TEST_DENIED; + } else { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } + } else { + ::CloseHandle(thread); + return SBOX_TEST_SUCCEEDED; + } +} + +SBOX_TESTS_COMMAND int OpenFile(int argc, wchar_t **argv) { + if (1 != argc) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + std::wstring path = argv[0]; + trim_quote(&path); + + return TestOpenReadFile(path); +} + +int TestOpenReadFile(const std::wstring& path) { + return TestOpenFile(path, false); +} + +int TestOpenWriteFile(int argc, wchar_t **argv) { + if (1 != argc) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + std::wstring path = argv[0]; + trim_quote(&path); + + return TestOpenWriteFile(path); + } + +int TestOpenWriteFile(const std::wstring& path) { + return TestOpenFile(path, true); +} + +SBOX_TESTS_COMMAND int OpenKey(int argc, wchar_t **argv) { + if (0 == argc || argc > 2) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + // Get the hive. + HKEY base_key = GetHKEYFromString(argv[0]); + + // Get the subkey. + std::wstring subkey; + if (2 == argc) { + subkey = argv[1]; + trim_quote(&subkey); + } + + return TestOpenKey(base_key, subkey); +} + +int TestOpenKey(HKEY base_key, std::wstring subkey) { + HKEY key; + LONG err_code = ::RegOpenKeyEx(base_key, + subkey.c_str(), + 0, // Reserved, must be 0. + MAXIMUM_ALLOWED, + &key); + if (ERROR_SUCCESS == err_code) { + ::RegCloseKey(key); + return SBOX_TEST_SUCCEEDED; + } else if (ERROR_INVALID_HANDLE == err_code || + ERROR_ACCESS_DENIED == err_code) { + return SBOX_TEST_DENIED; + } else { + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + } +} + +// Returns true if the current's thread desktop is the interactive desktop. +// In Vista there is a more direct test but for XP and w2k we need to check +// the object name. +bool IsInteractiveDesktop(bool* is_interactive) { + HDESK current_desk = ::GetThreadDesktop(::GetCurrentThreadId()); + if (NULL == current_desk) { + return false; + } + wchar_t current_desk_name[256] = {0}; + if (!::GetUserObjectInformationW(current_desk, UOI_NAME, current_desk_name, + sizeof(current_desk_name), NULL)) { + return false; + } + *is_interactive = (0 == _wcsicmp(L"default", current_desk_name)); + return true; +} + +SBOX_TESTS_COMMAND int OpenInteractiveDesktop(int, wchar_t **) { + return TestOpenInputDesktop(); +} + +int TestOpenInputDesktop() { + bool is_interactive = false; + if (IsInteractiveDesktop(&is_interactive) && is_interactive) { + return SBOX_TEST_SUCCEEDED; + } + HDESK desk = ::OpenInputDesktop(0, FALSE, DESKTOP_CREATEWINDOW); + if (desk) { + ::CloseDesktop(desk); + return SBOX_TEST_SUCCEEDED; + } + return SBOX_TEST_DENIED; +} + +SBOX_TESTS_COMMAND int SwitchToSboxDesktop(int, wchar_t **) { + return TestSwitchDesktop(); +} + +int TestSwitchDesktop() { + HDESK sbox_desk = ::GetThreadDesktop(::GetCurrentThreadId()); + if (NULL == sbox_desk) { + return SBOX_TEST_FAILED; + } + if (::SwitchDesktop(sbox_desk)) { + return SBOX_TEST_SUCCEEDED; + } + return SBOX_TEST_DENIED; +} + +SBOX_TESTS_COMMAND int SleepCmd(int argc, wchar_t **argv) { + if (1 != argc) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + ::Sleep(_wtoi(argv[0])); + return SBOX_TEST_SUCCEEDED; +} + + +} // namespace sandbox diff --git a/sandbox/win/tests/validation_tests/commands.h b/sandbox/win/tests/validation_tests/commands.h new file mode 100644 index 0000000..9b797a5 --- /dev/null +++ b/sandbox/win/tests/validation_tests/commands.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_TESTS_VALIDATION_TESTS_COMMANDS_H__ +#define SANDBOX_TESTS_VALIDATION_TESTS_COMMANDS_H__ + +namespace sandbox { + +// Checks if window is a real window. Returns a SboxTestResult. +int TestValidWindow(HWND window); + +// Tries to open the process_id. Returns a SboxTestResult. +int TestOpenProcess(DWORD process_id, DWORD access_mask); + +// Tries to open thread_id. Returns a SboxTestResult. +int TestOpenThread(DWORD thread_id); + +// Tries to open path for read access. Returns a SboxTestResult. +int TestOpenReadFile(const std::wstring& path); + +// Tries to open path for write access. Returns a SboxTestResult. +int TestOpenWriteFile(const std::wstring& path); + +// Tries to open a registry key. +int TestOpenKey(HKEY base_key, std::wstring subkey); + +// Tries to open the workstation's input desktop as long as the +// current desktop is not the interactive one. Returns a SboxTestResult. +int TestOpenInputDesktop(); + +// Tries to switch the interactive desktop. Returns a SboxTestResult. +int TestSwitchDesktop(); + +} // namespace sandbox + +#endif // SANDBOX_TESTS_VALIDATION_TESTS_COMMANDS_H__ diff --git a/sandbox/win/tests/validation_tests/sbox_validation_tests.vcproj b/sandbox/win/tests/validation_tests/sbox_validation_tests.vcproj new file mode 100644 index 0000000..9b7b599 --- /dev/null +++ b/sandbox/win/tests/validation_tests/sbox_validation_tests.vcproj @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/win/tests/validation_tests/suite.cc b/sandbox/win/tests/validation_tests/suite.cc new file mode 100644 index 0000000..3147f70 --- /dev/null +++ b/sandbox/win/tests/validation_tests/suite.cc @@ -0,0 +1,200 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains the validation tests for the sandbox. +// It includes the tests that need to be performed inside the +// sandbox. + +#include + +#include "base/win/windows_version.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/tests/common/controller.h" + +#pragma comment(lib, "shlwapi.lib") + +namespace { + +void TestProcessAccess(sandbox::TestRunner* runner, DWORD target) { + const wchar_t *kCommandTemplate = L"OpenProcessCmd %d %d"; + wchar_t command[1024] = {0}; + + // Test all the scary process permissions. + wsprintf(command, kCommandTemplate, target, PROCESS_CREATE_THREAD); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); + wsprintf(command, kCommandTemplate, target, PROCESS_DUP_HANDLE); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); + wsprintf(command, kCommandTemplate, target, PROCESS_SET_INFORMATION); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); + wsprintf(command, kCommandTemplate, target, PROCESS_VM_OPERATION); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); + wsprintf(command, kCommandTemplate, target, PROCESS_VM_READ); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); + wsprintf(command, kCommandTemplate, target, PROCESS_VM_WRITE); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); + wsprintf(command, kCommandTemplate, target, PROCESS_QUERY_INFORMATION); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); + wsprintf(command, kCommandTemplate, target, WRITE_DAC); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); + wsprintf(command, kCommandTemplate, target, WRITE_OWNER); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); + wsprintf(command, kCommandTemplate, target, READ_CONTROL); + EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command)); +} + +} // namespace + +namespace sandbox { + +// Returns true if the volume that contains any_path supports ACL security. The +// input path can contain unexpanded environment strings. Returns false on any +// failure or if the file system does not support file security (such as FAT). +bool VolumeSupportsACLs(const wchar_t* any_path) { + wchar_t expand[MAX_PATH +1]; + DWORD len =::ExpandEnvironmentStringsW(any_path, expand, _countof(expand)); + if (0 == len) return false; + if (len > _countof(expand)) return false; + if (!::PathStripToRootW(expand)) return false; + DWORD fs_flags = 0; + if (!::GetVolumeInformationW(expand, NULL, 0, 0, NULL, &fs_flags, NULL, 0)) + return false; + if (fs_flags & FILE_PERSISTENT_ACLS) return true; + return false; +} + +// Tests if the suite is working properly. +TEST(ValidationSuite, TestSuite) { + TestRunner runner; + ASSERT_EQ(SBOX_TEST_PING_OK, runner.RunTest(L"ping")); +} + +// Tests if the file system is correctly protected by the sandbox. +TEST(ValidationSuite, TestFileSystem) { + // Do not perform the test if the system is using FAT or any other + // file system that does not have file security. + ASSERT_TRUE(VolumeSupportsACLs(L"%SystemDrive%\\")); + ASSERT_TRUE(VolumeSupportsACLs(L"%SystemRoot%\\")); + ASSERT_TRUE(VolumeSupportsACLs(L"%ProgramFiles%\\")); + ASSERT_TRUE(VolumeSupportsACLs(L"%Temp%\\")); + ASSERT_TRUE(VolumeSupportsACLs(L"%AppData%\\")); + + TestRunner runner; + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %SystemDrive%")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %SystemRoot%")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %ProgramFiles%")); + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"OpenFile %SystemRoot%\\System32")); + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"OpenFile %SystemRoot%\\explorer.exe")); + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"OpenFile %SystemRoot%\\Cursors\\arrow_i.cur")); + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest(L"OpenFile %AllUsersProfile%")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %Temp%")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFile %AppData%")); +} + +// Tests if the registry is correctly protected by the sandbox. +TEST(ValidationSuite, TestRegistry) { + TestRunner runner; + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenKey HKLM")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenKey HKCU")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenKey HKU")); + EXPECT_EQ(SBOX_TEST_DENIED, + runner.RunTest( + L"OpenKey HKLM " + L"\"Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon\"")); +} + +// Tests that the permissions on the Windowstation does not allow the sandbox +// to get to the interactive desktop or to make the sbox desktop interactive. +TEST(ValidationSuite, TestDesktop) { + TestRunner runner; + runner.GetPolicy()->SetAlternateDesktop(false); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenInteractiveDesktop NULL")); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"SwitchToSboxDesktop NULL")); +} + +// Tests if the windows are correctly protected by the sandbox. +TEST(ValidationSuite, TestWindows) { + TestRunner runner; + wchar_t command[1024] = {0}; + + wsprintf(command, L"ValidWindow %d", ::GetDesktopWindow()); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); + + wsprintf(command, L"ValidWindow %d", ::FindWindow(NULL, NULL)); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); +} + +// Tests that a locked-down process cannot open another locked-down process. +TEST(ValidationSuite, TestProcessDenyLockdown) { + TestRunner runner; + TestRunner target; + wchar_t command[1024] = {0}; + + target.SetAsynchronous(true); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"SleepCmd 30000")); + + TestProcessAccess(&runner, target.process_id()); +} + +// Tests that a low-integrity process cannot open a locked-down process (due +// to the integrity label changing after startup via SetDelayedIntegrityLevel). +TEST(ValidationSuite, TestProcessDenyLowIntegrity) { + // This test applies only to Vista and above. + if (base::win::Version() < base::win::VERSION_VISTA) + return; + + TestRunner runner; + TestRunner target; + wchar_t command[1024] = {0}; + + target.SetAsynchronous(true); + target.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW); + + runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW); + runner.GetPolicy()->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS, + USER_INTERACTIVE); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"SleepCmd 30000")); + + TestProcessAccess(&runner, target.process_id()); +} + +// Tests that a locked-down process cannot open a low-integrity process. +TEST(ValidationSuite, TestProcessDenyBelowLowIntegrity) { + // This test applies only to Vista and above. + if (base::win::Version() < base::win::VERSION_VISTA) + return; + + TestRunner runner; + TestRunner target; + wchar_t command[1024] = {0}; + + target.SetAsynchronous(true); + target.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW); + target.GetPolicy()->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS, + USER_INTERACTIVE); + + runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_UNTRUSTED); + runner.GetPolicy()->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS, + USER_INTERACTIVE); + + EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"SleepCmd 30000")); + + TestProcessAccess(&runner, target.process_id()); +} + +// Tests if the threads are correctly protected by the sandbox. +TEST(ValidationSuite, TestThread) { + TestRunner runner; + wchar_t command[1024] = {0}; + + wsprintf(command, L"OpenThreadCmd %d", ::GetCurrentThreadId()); + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); +} + +} // namespace sandbox diff --git a/sandbox/win/tests/validation_tests/unit_tests.cc b/sandbox/win/tests/validation_tests/unit_tests.cc new file mode 100644 index 0000000..490f7ec --- /dev/null +++ b/sandbox/win/tests/validation_tests/unit_tests.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/tests/common/controller.h" + +int wmain(int argc, wchar_t **argv) { + if (argc >= 2) { + if (0 == _wcsicmp(argv[1], L"-child")) + return sandbox::DispatchCall(argc, argv); + } + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/sandbox/win/tools/finder/finder.cc b/sandbox/win/tools/finder/finder.cc new file mode 100644 index 0000000..26fc1b9 --- /dev/null +++ b/sandbox/win/tools/finder/finder.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/restricted_token.h" +#include "sandbox/src/restricted_token_utils.h" +#include "sandbox/tools/finder/finder.h" + +Finder::Finder() { + file_output_ = NULL; + object_type_ = 0; + access_type_ = 0; + token_handle_ = NULL; + memset(filesystem_stats_, 0, sizeof(filesystem_stats_)); + memset(registry_stats_, 0, sizeof(registry_stats_)); + memset(kernel_object_stats_, 0, sizeof(kernel_object_stats_)); +} + +Finder::~Finder() { + if (token_handle_) + ::CloseHandle(token_handle_); +} + +DWORD Finder::Init(sandbox::TokenLevel token_type, + DWORD object_type, + DWORD access_type, + FILE *file_output) { + DWORD err_code = ERROR_SUCCESS; + + err_code = InitNT(); + if (ERROR_SUCCESS != err_code) + return err_code; + + object_type_ = object_type; + access_type_ = access_type; + file_output_ = file_output; + + err_code = sandbox::CreateRestrictedToken(&token_handle_, token_type, + sandbox::INTEGRITY_LEVEL_LAST, + sandbox::PRIMARY); + return err_code; +} + +DWORD Finder::Scan() { + if (!token_handle_) { + return ERROR_NO_TOKEN; + } + + if (object_type_ & kScanRegistry) { + ParseRegistry(HKEY_LOCAL_MACHINE, L"HKLM\\"); + ParseRegistry(HKEY_USERS, L"HKU\\"); + ParseRegistry(HKEY_CURRENT_CONFIG, L"HKCC\\"); + } + + if (object_type_ & kScanFileSystem) { + ParseFileSystem(L"\\\\?\\C:"); + } + + if (object_type_ & kScanKernelObjects) { + ParseKernelObjects(L"\\"); + } + + return ERROR_SUCCESS; +} diff --git a/sandbox/win/tools/finder/finder.h b/sandbox/win/tools/finder/finder.h new file mode 100644 index 0000000..0686b1d --- /dev/null +++ b/sandbox/win/tools/finder/finder.h @@ -0,0 +1,143 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_TOOLS_FINDER_FINDER_H__ +#define SANDBOX_TOOLS_FINDER_FINDER_H__ + +#include "sandbox/src/restricted_token_utils.h" +#include "sandbox/tools/finder/ntundoc.h" + +// Type of stats that we calculate during the Scan operation +enum Stats { + READ = 0, // Number of objects with read access + WRITE, // Number of objects with write access + ALL, // Number of objects with r/w access + PARSE, // Number of objects parsed + BROKEN, // Number of errors while parsing the objects + SIZE_STATS // size of the enum +}; + +const int kScanRegistry = 0x01; +const int kScanFileSystem = 0x02; +const int kScanKernelObjects = 0x04; + +const int kTestForRead = 0x01; +const int kTestForWrite = 0x02; +const int kTestForAll = 0x04; + +#define FS_ERR L"FILE-ERROR" +#define OBJ_ERR L"OBJ-ERROR" +#define REG_ERR L"REG_ERROR" +#define OBJ L"OBJ" +#define FS L"FILE" +#define REG L"REG" + +// The impersonater class will impersonate a token when the object is created +// and revert when the object is going out of scope. +class Impersonater { + public: + Impersonater(HANDLE token_handle) { + if (token_handle) + ::ImpersonateLoggedOnUser(token_handle); + }; + ~Impersonater() { + ::RevertToSelf(); + }; +}; + +// The finder class handles the search of objects (file system, registry, kernel +// objects) on the system that can be opened by a restricted token. It can +// support multiple levels of restriction for the restricted token and can check +// for read, write or r/w access. It outputs the results to a file or stdout. +class Finder { + public: + Finder(); + ~Finder(); + DWORD Init(sandbox::TokenLevel token_type, DWORD object_type, + DWORD access_type, FILE *file_output); + DWORD Scan(); + + private: + // Parses a file system path and perform an access check on all files and + // folder found. + // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the + // win32 error code associated with the error. + DWORD ParseFileSystem(ATL::CString path); + + // Parses a registry hive referenced by "key" and performs an access check on + // all subkeys found. + // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the + // win32 error code associated with the error. + DWORD ParseRegistry(HKEY key, ATL::CString print_name); + + // Parses the kernel namespace beginning at "path" and performs an access + // check on all objects found. However, only some object types are supported, + // all non supported objects are ignored. + // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the + // win32 error code associated with the error. + DWORD ParseKernelObjects(ATL::CString path); + + // Checks if "path" can be accessed with the restricted token. + // Returns the access granted. + DWORD TestFileAccess(ATL::CString path); + + // Checks if the registry key with the path key\name can be accessed with the + // restricted token. + // print_name is only use for logging purpose. + // Returns the access granted. + DWORD TestRegAccess(HKEY key, ATL::CString name, ATL::CString print_name); + + // Checks if the kernel object "path" of type "type" can be accessed with + // the restricted token. + // Returns the access granted. + DWORD TestKernelObjectAccess(ATL::CString path, ATL::CString type); + + // Outputs information to the logfile + void Output(ATL::CString type, ATL::CString access, ATL::CString info) { + fprintf(file_output_, "\n%S;%S;%S", type.GetBuffer(), access.GetBuffer(), + info.GetBuffer()); + }; + + // Output information to the log file. + void Output(ATL::CString type, DWORD error, ATL::CString info) { + fprintf(file_output_, "\n%S;0x%X;%S", type.GetBuffer(), error, + info.GetBuffer()); + }; + + // Set func_to_call to the function pointer of the function used to handle + // requests for the kernel objects of type "type". If the type is not + // supported at the moment the function returns false and the func_to_call + // parameter is not modified. + bool GetFunctionForType(ATL::CString type, NTGENERICOPEN * func_to_call); + + // Initializes the NT function pointers to be able to use all the needed + // functions in NTDDL. + // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the + // win32 error code associated with the error. + DWORD InitNT(); + + // Calls func_to_call with the parameters desired_access, object_attributes + // and handle. func_to_call is a pointer to a function to open a kernel + // object. + NTSTATUS NtGenericOpen(ACCESS_MASK desired_access, + OBJECT_ATTRIBUTES *object_attributes, + NTGENERICOPEN func_to_call, + HANDLE *handle); + + // Type of object to check for. + DWORD object_type_; + // Access to try. + DWORD access_type_; + // Output file for the results. + FILE * file_output_; + // Handle to the restricted token. + HANDLE token_handle_; + // Stats containing the number of operations performed on the different + // objects. + int filesystem_stats_[SIZE_STATS]; + int registry_stats_[SIZE_STATS]; + int kernel_object_stats_[SIZE_STATS]; +}; + +#endif // SANDBOX_TOOLS_FINDER_FINDER_H__ diff --git a/sandbox/win/tools/finder/finder.vcproj b/sandbox/win/tools/finder/finder.vcproj new file mode 100644 index 0000000..787c847 --- /dev/null +++ b/sandbox/win/tools/finder/finder.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/win/tools/finder/finder_fs.cc b/sandbox/win/tools/finder/finder_fs.cc new file mode 100644 index 0000000..7375b86 --- /dev/null +++ b/sandbox/win/tools/finder/finder_fs.cc @@ -0,0 +1,117 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/restricted_token.h" +#include "sandbox/src/restricted_token_utils.h" +#include "sandbox/tools/finder/finder.h" + +DWORD Finder::ParseFileSystem(ATL::CString directory) { + WIN32_FIND_DATA find_data; + HANDLE find; + + //Search for items in the directory. + ATL::CString name_to_search = directory + L"\\*"; + find = ::FindFirstFile(name_to_search, &find_data); + if (INVALID_HANDLE_VALUE == find) { + DWORD error = ::GetLastError(); + Output(FS_ERR, error, directory); + filesystem_stats_[BROKEN]++; + return error; + } + + // parse all files or folders. + do { + if (_tcscmp(find_data.cFileName, L".") == 0 || + _tcscmp(find_data.cFileName, L"..") == 0) + continue; + + ATL::CString complete_name = directory + L"\\" + find_data.cFileName; + TestFileAccess(complete_name); + + // Call recursively the function if the path found is a directory. + if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { + ParseFileSystem(complete_name); + } + } while (::FindNextFile(find, &find_data) != 0); + + DWORD err_code = ::GetLastError(); + ::FindClose(find); + + if (ERROR_NO_MORE_FILES != err_code) { + Output(FS_ERR, err_code, directory); + filesystem_stats_[BROKEN]++; + return err_code; + } + + return ERROR_SUCCESS; +} + +DWORD Finder::TestFileAccess(ATL::CString name) { + Impersonater impersonate(token_handle_); + + filesystem_stats_[PARSE]++; + + HANDLE file; + if (access_type_ & kTestForAll) { + file = ::CreateFile(name.GetBuffer(), + GENERIC_ALL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (file != INVALID_HANDLE_VALUE) { + filesystem_stats_[ALL]++; + Output(FS, L"R/W", name.GetBuffer()); + ::CloseHandle(file); + return GENERIC_ALL; + } else if (::GetLastError() != ERROR_ACCESS_DENIED) { + Output(FS_ERR, GetLastError(), name); + filesystem_stats_[BROKEN]++; + } + } + + if (access_type_ & kTestForWrite) { + file = ::CreateFile(name.GetBuffer(), + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (file != INVALID_HANDLE_VALUE) { + filesystem_stats_[WRITE]++; + Output(FS, L"W", name); + ::CloseHandle(file); + return GENERIC_WRITE; + } else if (::GetLastError() != ERROR_ACCESS_DENIED) { + Output(FS_ERR, ::GetLastError(), name); + filesystem_stats_[BROKEN]++; + } + } + + if (access_type_ & kTestForRead) { + file = ::CreateFile(name.GetBuffer(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (file != INVALID_HANDLE_VALUE) { + filesystem_stats_[READ]++; + Output(FS, L"R", name); + ::CloseHandle(file); + return GENERIC_READ; + } else if (::GetLastError() != ERROR_ACCESS_DENIED) { + Output(FS_ERR, GetLastError(), name); + filesystem_stats_[BROKEN]++; + } + } + + return 0; +} diff --git a/sandbox/win/tools/finder/finder_kernel.cc b/sandbox/win/tools/finder/finder_kernel.cc new file mode 100644 index 0000000..66ab454 --- /dev/null +++ b/sandbox/win/tools/finder/finder_kernel.cc @@ -0,0 +1,248 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/restricted_token.h" +#include "sandbox/src/restricted_token_utils.h" +#include "sandbox/tools/finder/finder.h" +#include "sandbox/tools/finder/ntundoc.h" + +#define BUFFER_SIZE 0x800 +#define CHECKPTR(x) if (!x) return ::GetLastError() + +// NT API +NTQUERYDIRECTORYOBJECT NtQueryDirectoryObject; +NTOPENDIRECTORYOBJECT NtOpenDirectoryObject; +NTOPENEVENT NtOpenEvent; +NTOPENJOBOBJECT NtOpenJobObject; +NTOPENKEYEDEVENT NtOpenKeyedEvent; +NTOPENMUTANT NtOpenMutant; +NTOPENSECTION NtOpenSection; +NTOPENSEMAPHORE NtOpenSemaphore; +NTOPENSYMBOLICLINKOBJECT NtOpenSymbolicLinkObject; +NTOPENTIMER NtOpenTimer; +NTOPENFILE NtOpenFile; +NTCLOSE NtClose; + +DWORD Finder::InitNT() { + HMODULE ntdll_handle = ::LoadLibrary(L"ntdll.dll"); + CHECKPTR(ntdll_handle); + + NtOpenSymbolicLinkObject = (NTOPENSYMBOLICLINKOBJECT) ::GetProcAddress( + ntdll_handle, "NtOpenSymbolicLinkObject"); + CHECKPTR(NtOpenSymbolicLinkObject); + + NtQueryDirectoryObject = (NTQUERYDIRECTORYOBJECT) ::GetProcAddress( + ntdll_handle, "NtQueryDirectoryObject"); + CHECKPTR(NtQueryDirectoryObject); + + NtOpenDirectoryObject = (NTOPENDIRECTORYOBJECT) ::GetProcAddress( + ntdll_handle, "NtOpenDirectoryObject"); + CHECKPTR(NtOpenDirectoryObject); + + NtOpenKeyedEvent = (NTOPENKEYEDEVENT) ::GetProcAddress( + ntdll_handle, "NtOpenKeyedEvent"); + CHECKPTR(NtOpenKeyedEvent); + + NtOpenJobObject = (NTOPENJOBOBJECT) ::GetProcAddress( + ntdll_handle, "NtOpenJobObject"); + CHECKPTR(NtOpenJobObject); + + NtOpenSemaphore = (NTOPENSEMAPHORE) ::GetProcAddress( + ntdll_handle, "NtOpenSemaphore"); + CHECKPTR(NtOpenSemaphore); + + NtOpenSection = (NTOPENSECTION) ::GetProcAddress( + ntdll_handle, "NtOpenSection"); + CHECKPTR(NtOpenSection); + + NtOpenMutant= (NTOPENMUTANT) ::GetProcAddress(ntdll_handle, "NtOpenMutant"); + CHECKPTR(NtOpenMutant); + + NtOpenEvent = (NTOPENEVENT) ::GetProcAddress(ntdll_handle, "NtOpenEvent"); + CHECKPTR(NtOpenEvent); + + NtOpenTimer = (NTOPENTIMER) ::GetProcAddress(ntdll_handle, "NtOpenTimer"); + CHECKPTR(NtOpenTimer); + + NtOpenFile = (NTOPENFILE) ::GetProcAddress(ntdll_handle, "NtOpenFile"); + CHECKPTR(NtOpenFile); + + NtClose = (NTCLOSE) ::GetProcAddress(ntdll_handle, "NtClose"); + CHECKPTR(NtClose); + + return ERROR_SUCCESS; +} + +DWORD Finder::ParseKernelObjects(ATL::CString path) { + UNICODE_STRING unicode_str; + unicode_str.Length = (USHORT)path.GetLength()*2; + unicode_str.MaximumLength = (USHORT)path.GetLength()*2+2; + unicode_str.Buffer = path.GetBuffer(); + + OBJECT_ATTRIBUTES path_attributes; + InitializeObjectAttributes(&path_attributes, + &unicode_str, + 0, // No Attributes + NULL, // No Root Directory + NULL); // No Security Descriptor + + + DWORD object_index = 0; + DWORD data_written = 0; + + // TODO(nsylvain): Do not use BUFFER_SIZE. Try to get the size + // dynamically. + OBJDIR_INFORMATION *object_directory_info = + (OBJDIR_INFORMATION*) ::HeapAlloc(GetProcessHeap(), + 0, + BUFFER_SIZE); + + HANDLE file_handle; + NTSTATUS status_code = NtOpenDirectoryObject(&file_handle, + DIRECTORY_QUERY, + &path_attributes); + if (status_code != 0) + return ERROR_UNIDENTIFIED_ERROR; + + status_code = NtQueryDirectoryObject(file_handle, + object_directory_info, + BUFFER_SIZE, + TRUE, // Get Next Index + TRUE, // Ignore Input Index + &object_index, + &data_written); + + if (status_code != 0) + return ERROR_UNIDENTIFIED_ERROR; + + while (NtQueryDirectoryObject(file_handle, object_directory_info, + BUFFER_SIZE, TRUE, FALSE, &object_index, + &data_written) == 0 ) { + ATL::CString cur_path(object_directory_info->ObjectName.Buffer, + object_directory_info->ObjectName.Length / sizeof(WCHAR)); + + ATL::CString cur_type(object_directory_info->ObjectTypeName.Buffer, + object_directory_info->ObjectTypeName.Length / sizeof(WCHAR)); + + ATL::CString new_path; + if (path == L"\\") { + new_path = path + cur_path; + } else { + new_path = path + L"\\" + cur_path; + } + + TestKernelObjectAccess(new_path, cur_type); + + // Call the function recursively for all subdirectories + if (cur_type == L"Directory") { + ParseKernelObjects(new_path); + } + } + + NtClose(file_handle); + return ERROR_SUCCESS; +} + +DWORD Finder::TestKernelObjectAccess(ATL::CString path, ATL::CString type) { + Impersonater impersonate(token_handle_); + + kernel_object_stats_[PARSE]++; + + NTGENERICOPEN func = NULL; + GetFunctionForType(type, &func); + + if (!func) { + kernel_object_stats_[BROKEN]++; + Output(OBJ_ERR, type + L" Unsupported", path); + return ERROR_UNSUPPORTED_TYPE; + } + + UNICODE_STRING unicode_str; + unicode_str.Length = (USHORT)path.GetLength()*2; + unicode_str.MaximumLength = (USHORT)path.GetLength()*2+2; + unicode_str.Buffer = path.GetBuffer(); + + OBJECT_ATTRIBUTES path_attributes; + InitializeObjectAttributes(&path_attributes, + &unicode_str, + 0, // No Attributes + NULL, // No Root Directory + NULL); // No Security Descriptor + + HANDLE handle; + NTSTATUS status_code = 0; + + if (access_type_ & kTestForAll) { + status_code = NtGenericOpen(GENERIC_ALL, &path_attributes, func, &handle); + if (STATUS_SUCCESS == status_code) { + kernel_object_stats_[ALL]++; + Output(OBJ, L"R/W", path); + NtClose(handle); + return GENERIC_ALL; + } else if (status_code != EXCEPTION_ACCESS_VIOLATION && + status_code != STATUS_ACCESS_DENIED) { + Output(OBJ_ERR, status_code, path); + kernel_object_stats_[BROKEN]++; + } + } + + if (access_type_ & kTestForWrite) { + status_code = NtGenericOpen(GENERIC_WRITE, &path_attributes, func, &handle); + if (STATUS_SUCCESS == status_code) { + kernel_object_stats_[WRITE]++; + Output(OBJ, L"W", path); + NtClose(handle); + return GENERIC_WRITE; + } else if (status_code != EXCEPTION_ACCESS_VIOLATION && + status_code != STATUS_ACCESS_DENIED) { + Output(OBJ_ERR, status_code, path); + kernel_object_stats_[BROKEN]++; + } + } + + if (access_type_ & kTestForRead) { + status_code = NtGenericOpen(GENERIC_READ, &path_attributes, func, &handle); + if (STATUS_SUCCESS == status_code) { + kernel_object_stats_[READ]++; + Output(OBJ, L"R", path); + NtClose(handle); + return GENERIC_READ; + } else if (status_code != EXCEPTION_ACCESS_VIOLATION && + status_code != STATUS_ACCESS_DENIED) { + Output(OBJ_ERR, status_code, path); + kernel_object_stats_[BROKEN]++; + } + } + + return 0; +} + +NTSTATUS Finder::NtGenericOpen(ACCESS_MASK desired_access, + OBJECT_ATTRIBUTES *object_attributes, + NTGENERICOPEN func_to_call, + HANDLE *handle) { + return func_to_call(handle, desired_access, object_attributes); +} + +bool Finder::GetFunctionForType(ATL::CString type, + NTGENERICOPEN * func_to_call) { + NTGENERICOPEN func = NULL; + + if (type == L"Event") func = NtOpenEvent; + else if (type == L"Job") func = NtOpenJobObject; + else if (type == L"KeyedEvent") func = NtOpenKeyedEvent; + else if (type == L"Mutant") func = NtOpenMutant; + else if (type == L"Section") func = NtOpenSection; + else if (type == L"Semaphore") func = NtOpenSemaphore; + else if (type == L"Timer") func = NtOpenTimer; + else if (type == L"SymbolicLink") func = NtOpenSymbolicLinkObject; + else if (type == L"Directory") func = NtOpenDirectoryObject; + + if (func) { + *func_to_call = func; + return true; + } + + return false; +} diff --git a/sandbox/win/tools/finder/finder_registry.cc b/sandbox/win/tools/finder/finder_registry.cc new file mode 100644 index 0000000..4b18625 --- /dev/null +++ b/sandbox/win/tools/finder/finder_registry.cc @@ -0,0 +1,93 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/restricted_token.h" +#include "sandbox/src/restricted_token_utils.h" +#include "sandbox/tools/finder/finder.h" + +DWORD Finder::ParseRegistry(HKEY key, ATL::CString print_name) { + DWORD index = 0; + DWORD name_size = 2048; + wchar_t buffer[2048] = {0}; + // TODO(nsylvain): Don't hardcode 2048. Get the key len by calling the + // function. + LONG err_code = ::RegEnumKey(key, index, buffer, name_size); + while (ERROR_SUCCESS == err_code) { + ATL::CString name_complete = print_name + buffer + L"\\"; + TestRegAccess(key, buffer, name_complete); + + // Call the function recursively to parse all subkeys + HKEY key_to_parse; + err_code = ::RegOpenKeyEx(key, buffer, 0, KEY_ENUMERATE_SUB_KEYS, + &key_to_parse); + if (ERROR_SUCCESS == err_code) { + ParseRegistry(key_to_parse, name_complete); + ::RegCloseKey(key_to_parse); + } else { + registry_stats_[BROKEN]++; + Output(REG_ERR, err_code, name_complete); + } + + index++; + err_code = ::RegEnumKey(key, index, buffer, name_size); + } + + if (ERROR_NO_MORE_ITEMS != err_code) { + registry_stats_[BROKEN]++; + Output(REG_ERR, err_code, print_name); + } + + return ERROR_SUCCESS; +} + +DWORD Finder::TestRegAccess(HKEY key, ATL::CString name, + ATL::CString print_name) { + Impersonater impersonate(token_handle_); + + registry_stats_[PARSE]++; + + HKEY key_res; + LONG err_code = 0; + + if (access_type_ & kTestForAll) { + err_code = ::RegOpenKeyEx(key, name, 0, GENERIC_ALL, &key_res); + if (ERROR_SUCCESS == err_code) { + registry_stats_[ALL]++; + Output(REG, L"R/W", print_name); + ::RegCloseKey(key_res); + return GENERIC_ALL; + } else if (err_code != ERROR_ACCESS_DENIED) { + Output(REG_ERR, err_code, print_name); + registry_stats_[BROKEN]++; + } + } + + if (access_type_ & kTestForWrite) { + err_code = ::RegOpenKeyEx(key, name, 0, GENERIC_WRITE, &key_res); + if (ERROR_SUCCESS == err_code) { + registry_stats_[WRITE]++; + Output(REG, L"W", print_name); + ::RegCloseKey(key_res); + return GENERIC_WRITE; + } else if (err_code != ERROR_ACCESS_DENIED) { + Output(REG_ERR, err_code, print_name); + registry_stats_[BROKEN]++; + } + } + + if (access_type_ & kTestForRead) { + err_code = ::RegOpenKeyEx(key, name, 0, GENERIC_READ, &key_res); + if (ERROR_SUCCESS == err_code) { + registry_stats_[READ]++; + Output(REG, L"R", print_name); + ::RegCloseKey(key_res); + return GENERIC_READ; + } else if (err_code != ERROR_ACCESS_DENIED) { + Output(REG_ERR, err_code, print_name); + registry_stats_[BROKEN]++; + } + } + + return 0; +} diff --git a/sandbox/win/tools/finder/main.cc b/sandbox/win/tools/finder/main.cc new file mode 100644 index 0000000..6ffbec3 --- /dev/null +++ b/sandbox/win/tools/finder/main.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/restricted_token_utils.h" +#include "sandbox/tools/finder/finder.h" + +#define PARAM_IS(y) (argc > i) && (_wcsicmp(argv[i], y) == 0) + +void PrintUsage(wchar_t *application_name) { + wprintf(L"\n\nUsage: \n %ls --token type --object ob1 [ob2 ob3] " + L"--access ac1 [ac2 ac3] [--log filename]", application_name); + wprintf(L"\n\n Token Types : \n\tLOCKDOWN \n\tRESTRICTED " + L"\n\tLIMITED_USER \n\tINTERACTIVE_USER \n\tNON_ADMIN \n\tUNPROTECTED"); + wprintf(L"\n Object Types: \n\tREG \n\tFILE \n\tKERNEL"); + wprintf(L"\n Access Types: \n\tR \n\tW \n\tALL"); + wprintf(L"\n\nSample: \n %ls --token LOCKDOWN --object REG FILE KERNEL " + L"--access R W ALL", application_name); +} + +int wmain(int argc, wchar_t* argv[]) { + // Extract the filename from the path. + wchar_t *app_name = wcsrchr(argv[0], L'\\'); + if (!app_name) { + app_name = argv[0]; + } else { + app_name++; + } + + // parameters to read + ATL::CString log_file; + sandbox::TokenLevel token_type = sandbox::USER_LOCKDOWN; + DWORD object_type = 0; + DWORD access_type = 0; + + // no arguments + if (argc == 1) { + PrintUsage(app_name); + return -1; + } + + // parse command line. + for (int i = 1; i < argc; ++i) { + if (PARAM_IS(L"--token")) { + i++; + if (argc > i) { + if (PARAM_IS(L"LOCKDOWN")) { + token_type = sandbox::USER_LOCKDOWN; + } else if (PARAM_IS(L"RESTRICTED")) { + token_type = sandbox::USER_RESTRICTED; + } else if (PARAM_IS(L"LIMITED_USER")) { + token_type = sandbox::USER_LIMITED; + } else if (PARAM_IS(L"INTERACTIVE_USER")) { + token_type = sandbox::USER_INTERACTIVE; + } else if (PARAM_IS(L"NON_ADMIN")) { + token_type = sandbox::USER_NON_ADMIN; + } else if (PARAM_IS(L"USER_RESTRICTED_SAME_ACCESS")) { + token_type = sandbox::USER_RESTRICTED_SAME_ACCESS; + } else if (PARAM_IS(L"UNPROTECTED")) { + token_type = sandbox::USER_UNPROTECTED; + } else { + wprintf(L"\nAbord. Invalid token type \"%ls\"", argv[i]); + PrintUsage(app_name); + return -1; + } + } + } else if (PARAM_IS(L"--object")) { + bool is_object = true; + do { + i++; + if (PARAM_IS(L"REG")) { + object_type |= kScanRegistry; + } else if (PARAM_IS(L"FILE")) { + object_type |= kScanFileSystem; + } else if (PARAM_IS(L"KERNEL")) { + object_type |= kScanKernelObjects; + } else { + is_object = false; + } + } while(is_object); + i--; + } else if (PARAM_IS(L"--access")) { + bool is_access = true; + do { + i++; + if (PARAM_IS(L"R")) { + access_type |= kTestForRead; + } else if (PARAM_IS(L"W")) { + access_type |= kTestForWrite; + } else if (PARAM_IS(L"ALL")) { + access_type |= kTestForAll; + } else { + is_access = false; + } + } while(is_access); + i--; + } else if (PARAM_IS(L"--log")) { + i++; + if (argc > i) { + log_file = argv[i]; + } + else { + wprintf(L"\nAbord. No log file specified"); + PrintUsage(app_name); + return -1; + } + } else { + wprintf(L"\nAbord. Unrecognized parameter \"%ls\"", argv[i]); + PrintUsage(app_name); + return -1; + } + } + + // validate parameters + if (0 == access_type) { + wprintf(L"\nAbord, Access type not specified"); + PrintUsage(app_name); + return -1; + } + + if (0 == object_type) { + wprintf(L"\nAbord, Object type not specified"); + PrintUsage(app_name); + return -1; + } + + + // Open log file + FILE * file_output; + if (log_file.GetLength()) { + errno_t err = _wfopen_s(&file_output, log_file, L"w"); + if (err) { + wprintf(L"\nAbord, Cannot open file \"%ls\"", log_file.GetBuffer()); + return -1; + } + } else { + file_output = stdout; + } + + Finder finder_obj; + finder_obj.Init(token_type, object_type, access_type, file_output); + finder_obj.Scan(); + + fclose(file_output); + + return 0; +} diff --git a/sandbox/win/tools/finder/ntundoc.h b/sandbox/win/tools/finder/ntundoc.h new file mode 100644 index 0000000..dc8c3a5 --- /dev/null +++ b/sandbox/win/tools/finder/ntundoc.h @@ -0,0 +1,275 @@ +// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_TOOLS_FINDER_NTUNDOC_H__ +#define SANDBOX_TOOLS_FINDER_NTUNDOC_H__ + +#define NTSTATUS ULONG +#define STATUS_SUCCESS 0x00000000 +#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004 +#define STATUS_ACCESS_DENIED 0xC0000022 +#define STATUS_BUFFER_OVERFLOW 0x80000005 + +typedef struct _LSA_UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; + +typedef struct _OBJDIR_INFORMATION { + UNICODE_STRING ObjectName; + UNICODE_STRING ObjectTypeName; + BYTE Data[1]; +} OBJDIR_INFORMATION; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + UNICODE_STRING *ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES; + +typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION { + ULONG Attributes; + ACCESS_MASK GrantedAccess; + ULONG HandleCount; + ULONG PointerCount; + ULONG Reserved[10]; // reserved for internal use + } PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION; + +typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION { + UNICODE_STRING TypeName; + ULONG Reserved [22]; // reserved for internal use +} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION; + +typedef enum _POOL_TYPE { + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + ReservedType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS +} POOL_TYPE; + +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING Name; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccess; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + USHORT MaintainTypeList; + POOL_TYPE PoolType; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + +typedef struct _OBJECT_NAME_INFORMATION { + UNICODE_STRING ObjectName; +} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; + +typedef enum _OBJECT_INFORMATION_CLASS { + ObjectBasicInformation, + ObjectNameInformation, + ObjectTypeInformation, + ObjectAllInformation, + ObjectDataInformation +} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS; + +typedef struct _FILE_NAME_INFORMATION { + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; + +typedef enum _FILE_INFORMATION_CLASS { + // end_wdm + FileDirectoryInformation = 1, + FileFullDirectoryInformation, // 2 + FileBothDirectoryInformation, // 3 + FileBasicInformation, // 4 wdm + FileStandardInformation, // 5 wdm + FileInternalInformation, // 6 + FileEaInformation, // 7 + FileAccessInformation, // 8 + FileNameInformation, // 9 + FileRenameInformation, // 10 + FileLinkInformation, // 11 + FileNamesInformation, // 12 + FileDispositionInformation, // 13 + FilePositionInformation, // 14 wdm + FileFullEaInformation, // 15 + FileModeInformation, // 16 + FileAlignmentInformation, // 17 + FileAllInformation, // 18 + FileAllocationInformation, // 19 + FileEndOfFileInformation, // 20 wdm + FileAlternateNameInformation, // 21 + FileStreamInformation, // 22 + FilePipeInformation, // 23 + FilePipeLocalInformation, // 24 + FilePipeRemoteInformation, // 25 + FileMailslotQueryInformation, // 26 + FileMailslotSetInformation, // 27 + FileCompressionInformation, // 28 + FileObjectIdInformation, // 29 + FileCompletionInformation, // 30 + FileMoveClusterInformation, // 31 + FileQuotaInformation, // 32 + FileReparsePointInformation, // 33 + FileNetworkOpenInformation, // 34 + FileAttributeTagInformation, // 35 + FileTrackingInformation, // 36 + FileMaximumInformation + // begin_wdm +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemHandleInformation = 16 +} SYSTEM_INFORMATION_CLASS; + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +#define InitializeObjectAttributes( p, n, a, r, s ) { \ + (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ + (p)->RootDirectory = r; \ + (p)->Attributes = a; \ + (p)->ObjectName = n; \ + (p)->SecurityDescriptor = s; \ + (p)->SecurityQualityOfService = NULL; \ +} + +typedef struct _SYSTEM_HANDLE_INFORMATION { + USHORT ProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeNumber; + UCHAR Flags; + USHORT Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX { + ULONG NumberOfHandles; + SYSTEM_HANDLE_INFORMATION Information[1]; +} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; + +#define POBJECT_ATTRIBUTES OBJECT_ATTRIBUTES* + +typedef NTSTATUS (WINAPI* NTQUERYDIRECTORYOBJECT)( + HANDLE, + OBJDIR_INFORMATION*, + DWORD, + DWORD, + DWORD, + DWORD*, + DWORD*); + +typedef NTSTATUS (WINAPI* NTOPENDIRECTORYOBJECT)( + HANDLE *, + DWORD, + OBJECT_ATTRIBUTES* ); + +typedef NTSTATUS (WINAPI* NTGENERICOPEN) ( + OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NTOPENEVENT)( + OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NTOPENJOBOBJECT)( + OUT PHANDLE JobHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NTOPENKEYEDEVENT)( + OUT PHANDLE KeyedEventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NTOPENMUTANT)( + OUT PHANDLE MutantHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NTOPENSECTION)( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NTOPENSEMAPHORE)( + OUT PHANDLE SemaphoreHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NTOPENSYMBOLICLINKOBJECT)( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NTOPENTIMER)( + OUT PHANDLE TimerHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes); + +typedef NTSTATUS (WINAPI* NTOPENFILE)( + HANDLE *, + DWORD, + OBJECT_ATTRIBUTES *, + IO_STATUS_BLOCK *, + DWORD, + DWORD); + +typedef NTSTATUS (WINAPI* NTQUERYINFORMATIONFILE)( + HANDLE, + PIO_STATUS_BLOCK, + PVOID, + ULONG, + FILE_INFORMATION_CLASS); + +typedef NTSTATUS (WINAPI* NTQUERYSYSTEMINFORMATION)( + SYSTEM_INFORMATION_CLASS SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength); + +typedef NTSTATUS (WINAPI* NTQUERYOBJECT)( + HANDLE Handle, + OBJECT_INFORMATION_CLASS ObjectInformationClass, + PVOID ObjectInformation, + ULONG ObjectInformationLength, + PULONG ReturnLength); + +typedef NTSTATUS (WINAPI* NTCLOSE) (HANDLE); + +#define DIRECTORY_QUERY 0x0001 +#define DIRECTORY_TRAVERSE 0x0002 +#define DIRECTORY_CREATE_OBJECT 0x0004 +#define DIRECTORY_CREATE_SUBDIRECTORY 0x0008 +#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) + +#endif // SANDBOX_TOOLS_FINDER_NTUNDOC_H__ diff --git a/sandbox/win/tools/launcher/launcher.cc b/sandbox/win/tools/launcher/launcher.cc new file mode 100644 index 0000000..8f913b3 --- /dev/null +++ b/sandbox/win/tools/launcher/launcher.cc @@ -0,0 +1,156 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/src/restricted_token_utils.h" + +// launcher.exe is an application used to launch another application with a +// restricted token. This is to be used for testing only. +// The parameters are the level of security of the primary token, the +// impersonation token and the job object along with the command line to +// execute. +// See the usage (launcher.exe without parameters) for the correct format. + +#define PARAM_IS(y) (argc > i) && (_wcsicmp(argv[i], y) == 0) + +void PrintUsage(const wchar_t *application_name) { + wprintf(L"\n\nUsage: \n %ls --main level --init level --job level cmd_line ", + application_name); + wprintf(L"\n\n Levels : \n\tLOCKDOWN \n\tRESTRICTED " + L"\n\tLIMITED_USER \n\tINTERACTIVE_USER \n\tNON_ADMIN \n\tUNPROTECTED"); + wprintf(L"\n\n main: Security level of the main token"); + wprintf(L"\n init: Security level of the impersonation token"); + wprintf(L"\n job: Security level of the job object"); +} + +bool GetTokenLevelFromString(const wchar_t *param, + sandbox::TokenLevel* level) { + if (_wcsicmp(param, L"LOCKDOWN") == 0) { + *level = sandbox::USER_LOCKDOWN; + } else if (_wcsicmp(param, L"RESTRICTED") == 0) { + *level = sandbox::USER_RESTRICTED; + } else if (_wcsicmp(param, L"LIMITED_USER") == 0) { + *level = sandbox::USER_LIMITED; + } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) { + *level = sandbox::USER_INTERACTIVE; + } else if (_wcsicmp(param, L"NON_ADMIN") == 0) { + *level = sandbox::USER_NON_ADMIN; + } else if (_wcsicmp(param, L"USER_RESTRICTED_SAME_ACCESS") == 0) { + *level = sandbox::USER_RESTRICTED_SAME_ACCESS; + } else if (_wcsicmp(param, L"UNPROTECTED") == 0) { + *level = sandbox::USER_UNPROTECTED; + } else { + return false; + } + + return true; +} + +bool GetJobLevelFromString(const wchar_t *param, sandbox::JobLevel* level) { + if (_wcsicmp(param, L"LOCKDOWN") == 0) { + *level = sandbox::JOB_LOCKDOWN; + } else if (_wcsicmp(param, L"RESTRICTED") == 0) { + *level = sandbox::JOB_RESTRICTED; + } else if (_wcsicmp(param, L"LIMITED_USER") == 0) { + *level = sandbox::JOB_LIMITED_USER; + } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) { + *level = sandbox::JOB_INTERACTIVE; + } else if (_wcsicmp(param, L"NON_ADMIN") == 0) { + wprintf(L"\nNON_ADMIN is not a supported job type"); + return false; + } else if (_wcsicmp(param, L"UNPROTECTED") == 0) { + *level = sandbox::JOB_UNPROTECTED; + } else { + return false; + } + + return true; +} + +int wmain(int argc, wchar_t *argv[]) { + // Extract the filename from the path. + wchar_t *app_name = wcsrchr(argv[0], L'\\'); + if (!app_name) { + app_name = argv[0]; + } else { + app_name++; + } + + // no argument + if (argc == 1) { + PrintUsage(app_name); + return -1; + } + + sandbox::TokenLevel primary_level = sandbox::USER_LOCKDOWN; + sandbox::TokenLevel impersonation_level = + sandbox::USER_RESTRICTED_SAME_ACCESS; + sandbox::JobLevel job_level = sandbox::JOB_LOCKDOWN; + ATL::CString command_line; + + // parse command line. + for (int i = 1; i < argc; ++i) { + if (PARAM_IS(L"--main")) { + i++; + if (argc > i) { + if (!GetTokenLevelFromString(argv[i], &primary_level)) { + wprintf(L"\nAbord, Unrecognized main token level \"%ls\"", argv[i]); + PrintUsage(app_name); + return -1; + } + } + } else if (PARAM_IS(L"--init")) { + i++; + if (argc > i) { + if (!GetTokenLevelFromString(argv[i], &impersonation_level)) { + wprintf(L"\nAbord, Unrecognized init token level \"%ls\"", argv[i]); + PrintUsage(app_name); + return -1; + } + } + } else if (PARAM_IS(L"--job")) { + i++; + if (argc > i) { + if (!GetJobLevelFromString(argv[i], &job_level)) { + wprintf(L"\nAbord, Unrecognized job security level \"%ls\"", argv[i]); + PrintUsage(app_name); + return -1; + } + } + } else { + if (command_line.GetLength()) { + command_line += L' '; + } + command_line += argv[i]; + } + } + + if (!command_line.GetLength()) { + wprintf(L"\nAbord, No command line specified"); + PrintUsage(app_name); + return -1; + } + + wprintf(L"\nLaunching command line: \"%ls\"\n", command_line.GetBuffer()); + + HANDLE job_handle; + DWORD err_code = sandbox::StartRestrictedProcessInJob( + command_line.GetBuffer(), + primary_level, + impersonation_level, + job_level, + &job_handle); + if (ERROR_SUCCESS != err_code) { + wprintf(L"\nAbord, Error %d while launching command line.", err_code); + return -1; + } + + wprintf(L"\nPress any key to continue."); + while(!_kbhit()) { + Sleep(100); + } + + ::CloseHandle(job_handle); + + return 0; +} diff --git a/sandbox/win/tools/launcher/launcher.vcproj b/sandbox/win/tools/launcher/launcher.vcproj new file mode 100644 index 0000000..71ed011 --- /dev/null +++ b/sandbox/win/tools/launcher/launcher.vcproj @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/win/wow_helper.sln b/sandbox/win/wow_helper.sln new file mode 100644 index 0000000..26d0da2 --- /dev/null +++ b/sandbox/win/wow_helper.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wow_helper", "wow_helper\wow_helper.vcproj", "{BCF3A457-39F1-4DAA-9A65-93CFCD559036}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Debug|x64.ActiveCfg = Debug|x64 + {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Debug|x64.Build.0 = Debug|x64 + {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Release|x64.ActiveCfg = Release|x64 + {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/sandbox/win/wow_helper/service64_resolver.cc b/sandbox/win/wow_helper/service64_resolver.cc new file mode 100644 index 0000000..d54e0b0 --- /dev/null +++ b/sandbox/win/wow_helper/service64_resolver.cc @@ -0,0 +1,342 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/wow_helper/service64_resolver.h" + +#include "base/memory/scoped_ptr.h" +#include "sandbox/wow_helper/target_code.h" + +namespace { +#pragma pack(push, 1) + +const BYTE kMovEax = 0xB8; +const BYTE kMovEdx = 0xBA; +const USHORT kCallPtrEdx = 0x12FF; +const BYTE kRet = 0xC2; +const BYTE kNop = 0x90; +const USHORT kJmpEdx = 0xE2FF; +const USHORT kXorEcx = 0xC933; +const ULONG kLeaEdx = 0x0424548D; +const ULONG kCallFs1 = 0xC015FF64; +const ULONG kCallFs2Ret = 0xC2000000; +const BYTE kPopEdx = 0x5A; +const BYTE kPushEdx = 0x52; +const BYTE kPush32 = 0x68; + +const ULONG kMmovR10EcxMovEax = 0xB8D18B4C; +const USHORT kSyscall = 0x050F; +const BYTE kRetNp = 0xC3; +const BYTE kPad = 0x66; +const USHORT kNop16 = 0x9066; +const BYTE kRelJmp = 0xE9; + +const ULONG kXorRaxMovEax = 0xB8C03148; +const ULONG kSaveRcx = 0x10488948; +const ULONG kMovRcxRaxJmp = 0xE9C88B48; + +// Service code for 64 bit systems. +struct ServiceEntry { + // this struct contains roughly the following code: + // mov r10,rcx + // mov eax,52h + // syscall + // ret + // xchg ax,ax + // xchg ax,ax + + ULONG mov_r10_ecx_mov_eax; // = 4C 8B D1 B8 + ULONG service_id; + USHORT syscall; // = 0F 05 + BYTE ret; // = C3 + BYTE pad; // = 66 + USHORT xchg_ax_ax1; // = 66 90 + USHORT xchg_ax_ax2; // = 66 90 +}; + +struct Redirected { + // this struct contains roughly the following code: + // jmp relative_32 + // xchg ax,ax // 3 byte nop + + Redirected() { + jmp = kRelJmp; + relative = 0; + pad = kPad; + xchg_ax_ax = kNop16; + }; + BYTE jmp; // = E9 + ULONG relative; + BYTE pad; // = 66 + USHORT xchg_ax_ax; // = 66 90 +}; + +struct InternalThunk { + // this struct contains roughly the following code: + // xor rax,rax + // mov eax, 0x00080000 // Thunk storage. + // mov [rax]PatchInfo.service, rcx // Save first argument. + // mov rcx, rax + // jmp relative_to_interceptor + + InternalThunk() { + xor_rax_mov_eax = kXorRaxMovEax; + patch_info = 0; + save_rcx = kSaveRcx; + mov_rcx_rax_jmp = kMovRcxRaxJmp; + relative = 0; + }; + ULONG xor_rax_mov_eax; // = 48 31 C0 B8 + ULONG patch_info; + ULONG save_rcx; // = 48 89 48 10 + ULONG mov_rcx_rax_jmp; // = 48 8b c8 e9 + ULONG relative; +}; + +struct ServiceFullThunk { + sandbox::PatchInfo patch_info; + ServiceEntry original; + InternalThunk internal_thunk; +}; + +#pragma pack(pop) + +// Simple utility function to write to a buffer on the child, if the memery has +// write protection attributes. +// Arguments: +// child_process (in): process to write to. +// address (out): memory position on the child to write to. +// buffer (in): local buffer with the data to write . +// length (in): number of bytes to write. +// Returns true on success. +bool WriteProtectedChildMemory(HANDLE child_process, + void* address, + const void* buffer, + size_t length) { + // first, remove the protections + DWORD old_protection; + if (!::VirtualProtectEx(child_process, address, length, + PAGE_WRITECOPY, &old_protection)) + return false; + + SIZE_T written; + bool ok = ::WriteProcessMemory(child_process, address, buffer, length, + &written) && (length == written); + + // always attempt to restore the original protection + if (!::VirtualProtectEx(child_process, address, length, + old_protection, &old_protection)) + return false; + + return ok; +} + +// Get pointers to the functions that we need from ntdll.dll. +NTSTATUS ResolveNtdll(sandbox::PatchInfo* patch_info) { + wchar_t* ntdll_name = L"ntdll.dll"; + HMODULE ntdll = ::GetModuleHandle(ntdll_name); + if (!ntdll) + return STATUS_PROCEDURE_NOT_FOUND; + + void* signal = ::GetProcAddress(ntdll, "NtSignalAndWaitForSingleObject"); + if (!signal) + return STATUS_PROCEDURE_NOT_FOUND; + + patch_info->signal_and_wait = + reinterpret_cast(signal); + + return STATUS_SUCCESS; +} + +}; // namespace + +namespace sandbox { + +NTSTATUS ResolverThunk::Init(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes) { + if (NULL == thunk_storage || 0 == storage_bytes || + NULL == target_module || NULL == target_name) + return STATUS_INVALID_PARAMETER; + + if (storage_bytes < GetThunkSize()) + return STATUS_BUFFER_TOO_SMALL; + + NTSTATUS ret = STATUS_SUCCESS; + if (NULL == interceptor_entry_point) { + ret = ResolveInterceptor(interceptor_module, interceptor_name, + &interceptor_entry_point); + if (!NT_SUCCESS(ret)) + return ret; + } + + ret = ResolveTarget(target_module, target_name, &target_); + if (!NT_SUCCESS(ret)) + return ret; + + interceptor_ = interceptor_entry_point; + + return ret; +} + +NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module, + const char* interceptor_name, + const void** address) { + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS ResolverThunk::ResolveTarget(const void* module, + const char* function_name, + void** address) { + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS Service64ResolverThunk::Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used) { + NTSTATUS ret = Init(target_module, interceptor_module, target_name, + interceptor_name, interceptor_entry_point, + thunk_storage, storage_bytes); + if (!NT_SUCCESS(ret)) + return ret; + + size_t thunk_bytes = GetThunkSize(); + scoped_array thunk_buffer(new char[thunk_bytes]); + ServiceFullThunk* thunk = reinterpret_cast( + thunk_buffer.get()); + + if (!IsFunctionAService(&thunk->original)) + return STATUS_UNSUCCESSFUL; + + ret = PerformPatch(thunk, thunk_storage); + + if (NULL != storage_used) + *storage_used = thunk_bytes; + + return ret; +} + +NTSTATUS Service64ResolverThunk::ResolveInterceptor( + const void* interceptor_module, + const char* interceptor_name, + const void** address) { + // After all, we are using a locally mapped version of the exe, so the + // action is the same as for a target function. + return ResolveTarget(interceptor_module, interceptor_name, + const_cast(address)); +} + +// In this case all the work is done from the parent, so resolve is +// just a simple GetProcAddress. +NTSTATUS Service64ResolverThunk::ResolveTarget(const void* module, + const char* function_name, + void** address) { + if (NULL == module) + return STATUS_UNSUCCESSFUL; + + *address = ::GetProcAddress(bit_cast(module), function_name); + + if (NULL == *address) + return STATUS_UNSUCCESSFUL; + + return STATUS_SUCCESS; +} + +size_t Service64ResolverThunk::GetThunkSize() const { + return sizeof(ServiceFullThunk); +} + +bool Service64ResolverThunk::IsFunctionAService(void* local_thunk) const { + ServiceEntry function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) + return false; + + if (sizeof(function_code) != read) + return false; + + if (kMmovR10EcxMovEax != function_code.mov_r10_ecx_mov_eax || + kSyscall != function_code.syscall || kRetNp != function_code.ret) + return false; + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + + return true; +} + +NTSTATUS Service64ResolverThunk::PerformPatch(void* local_thunk, + void* remote_thunk) { + ServiceFullThunk* full_local_thunk = reinterpret_cast( + local_thunk); + ServiceFullThunk* full_remote_thunk = reinterpret_cast( + remote_thunk); + + // If the source or target are above 4GB we cannot do this relative jump. + if (reinterpret_cast(full_remote_thunk) > + static_cast(ULONG_MAX)) + return STATUS_CONFLICTING_ADDRESSES; + + if (reinterpret_cast(target_) > static_cast(ULONG_MAX)) + return STATUS_CONFLICTING_ADDRESSES; + + // Patch the original code. + Redirected local_service; + Redirected* remote_service = reinterpret_cast(target_); + ULONG_PTR diff = reinterpret_cast(&full_remote_thunk->internal_thunk) - + &remote_service->pad; + local_service.relative = static_cast(diff); + + // Setup the PatchInfo structure. + SIZE_T actual; + if (!::ReadProcessMemory(process_, remote_thunk, local_thunk, + sizeof(PatchInfo), &actual)) + return STATUS_UNSUCCESSFUL; + if (sizeof(PatchInfo) != actual) + return STATUS_UNSUCCESSFUL; + + full_local_thunk->patch_info.orig_MapViewOfSection = reinterpret_cast< + NtMapViewOfSectionFunction>(&full_remote_thunk->original); + full_local_thunk->patch_info.patch_location = target_; + NTSTATUS ret = ResolveNtdll(&full_local_thunk->patch_info); + if (!NT_SUCCESS(ret)) + return ret; + + // Setup the thunk. The jump out is performed from right after the end of the + // thunk (full_remote_thunk + 1). + InternalThunk my_thunk; + ULONG_PTR patch_info = reinterpret_cast(remote_thunk); + my_thunk.patch_info = static_cast(patch_info); + diff = reinterpret_cast(interceptor_) - + reinterpret_cast(full_remote_thunk + 1); + my_thunk.relative = static_cast(diff); + + memcpy(&full_local_thunk->internal_thunk, &my_thunk, sizeof(my_thunk)); + + // copy the local thunk buffer to the child + if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, + sizeof(ServiceFullThunk), &actual)) + return STATUS_UNSUCCESSFUL; + + if (sizeof(ServiceFullThunk) != actual) + return STATUS_UNSUCCESSFUL; + + // and now change the function to intercept, on the child + if (!::WriteProtectedChildMemory(process_, target_, &local_service, + sizeof(local_service))) + return STATUS_UNSUCCESSFUL; + + return STATUS_SUCCESS; +} + +} // namespace sandbox diff --git a/sandbox/win/wow_helper/service64_resolver.h b/sandbox/win/wow_helper/service64_resolver.h new file mode 100644 index 0000000..f782172 --- /dev/null +++ b/sandbox/win/wow_helper/service64_resolver.h @@ -0,0 +1,72 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__ +#define SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__ + +#include "sandbox/src/nt_internals.h" +#include "sandbox/src/resolver.h" + +namespace sandbox { + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll (64-bit). +class Service64ResolverThunk : public ResolverThunk { + public: + // The service resolver needs a child process to write to. + explicit Service64ResolverThunk(HANDLE process) + : process_(process), ntdll_base_(NULL) {} + virtual ~Service64ResolverThunk() {} + + // Implementation of Resolver::Setup. + virtual NTSTATUS Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used); + + // Implementation of Resolver::ResolveInterceptor. + virtual NTSTATUS ResolveInterceptor(const void* module, + const char* function_name, + const void** address); + + // Implementation of Resolver::ResolveTarget. + virtual NTSTATUS ResolveTarget(const void* module, + const char* function_name, + void** address); + + // Implementation of Resolver::GetThunkSize. + virtual size_t GetThunkSize() const; + + protected: + // The unit test will use this member to allow local patch on a buffer. + HMODULE ntdll_base_; + + // Handle of the child process. + HANDLE process_; + + private: + // Returns true if the code pointer by target_ corresponds to the expected + // type of function. Saves that code on the first part of the thunk pointed + // by local_thunk (should be directly accessible from the parent). + virtual bool IsFunctionAService(void* local_thunk) const; + + // Performs the actual patch of target_. + // local_thunk must be already fully initialized, and the first part must + // contain the original code. The real type of this buffer is ServiceFullThunk + // (yes, private). remote_thunk (real type ServiceFullThunk), must be + // allocated on the child, and will contain the thunk data, after this call. + // Returns the apropriate status code. + virtual NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk); + + DISALLOW_COPY_AND_ASSIGN(Service64ResolverThunk); +}; + +} // namespace sandbox + + +#endif // SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__ diff --git a/sandbox/win/wow_helper/target_code.cc b/sandbox/win/wow_helper/target_code.cc new file mode 100644 index 0000000..1f93988 --- /dev/null +++ b/sandbox/win/wow_helper/target_code.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/wow_helper/target_code.h" + +namespace sandbox { + +// Hooks NtMapViewOfSection to detect the load of dlls. +NTSTATUS WINAPI TargetNtMapViewOfSection( + PatchInfo *patch_info, HANDLE process, PVOID *base, ULONG_PTR zero_bits, + SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size, + SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect) { + NTSTATUS ret = patch_info->orig_MapViewOfSection(patch_info->section, process, + base, zero_bits, commit_size, + offset, view_size, inherit, + allocation_type, protect); + + LARGE_INTEGER timeout; + timeout.QuadPart = -(5 * 10000000); // 5 seconds. + + // The wait is alertable. + patch_info->signal_and_wait(patch_info->dll_load, patch_info->continue_load, + TRUE, &timeout); + + return ret; +} + +// Marks the end of the code to copy to the target process. +NTSTATUS WINAPI TargetEnd() { + return STATUS_SUCCESS; +} + +} // namespace sandbox diff --git a/sandbox/win/wow_helper/target_code.h b/sandbox/win/wow_helper/target_code.h new file mode 100644 index 0000000..56db6fe --- /dev/null +++ b/sandbox/win/wow_helper/target_code.h @@ -0,0 +1,41 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_WOW_HELPER_TARGET_CODE_H__ +#define SANDBOX_WOW_HELPER_TARGET_CODE_H__ + +#include "sandbox/src/nt_internals.h" + +namespace sandbox { + +extern "C" { + +// Holds the information needed for the interception of NtMapViewOfSection. +// Changes of this structure must be synchronized with changes of PatchInfo32 +// on sandbox/src/wow64.cc. +struct PatchInfo { + HANDLE dll_load; // Event to signal the broker. + HANDLE continue_load; // Event to wait for the broker. + HANDLE section; // First argument of the call. + NtMapViewOfSectionFunction orig_MapViewOfSection; + NtSignalAndWaitForSingleObjectFunction signal_and_wait; + void* patch_location; +}; + +// Interception of NtMapViewOfSection on the child process. +// It should never be called directly. This function provides the means to +// detect dlls being loaded, so we can patch them if needed. +NTSTATUS WINAPI TargetNtMapViewOfSection( + PatchInfo* patch_info, HANDLE process, PVOID* base, ULONG_PTR zero_bits, + SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size, + SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect); + +// Marker of the end of TargetNtMapViewOfSection. +NTSTATUS WINAPI TargetEnd(); + +} // extern "C" + +} // namespace sandbox + +#endif // SANDBOX_WOW_HELPER_TARGET_CODE_H__ diff --git a/sandbox/win/wow_helper/wow_helper.cc b/sandbox/win/wow_helper/wow_helper.cc new file mode 100644 index 0000000..6c272e4 --- /dev/null +++ b/sandbox/win/wow_helper/wow_helper.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Wow_helper.exe is a simple Win32 64-bit executable designed to help to +// sandbox a 32 bit application running on a 64 bit OS. The basic idea is to +// perform a 64 bit interception of the target process and notify the 32-bit +// broker process whenever a DLL is being loaded. This allows the broker to +// setup the interceptions (32-bit) properly on the target. + +#include + +#include + +#include "sandbox/wow_helper/service64_resolver.h" +#include "sandbox/wow_helper/target_code.h" + +namespace { + +// Grabbed from base/string_util.h +template +inline typename string_type::value_type* WriteInto(string_type* str, + size_t length_with_null) { + str->reserve(length_with_null); + str->resize(length_with_null - 1); + return &((*str)[0]); +} + +// Grabbed from base/string_util.cc +std::string WideToMultiByte(const std::wstring& wide, UINT code_page) { + if (wide.length() == 0) + return std::string(); + + // compute the length of the buffer we'll need + int charcount = WideCharToMultiByte(code_page, 0, wide.c_str(), -1, + NULL, 0, NULL, NULL); + if (charcount == 0) + return std::string(); + + // convert + std::string mb; + WideCharToMultiByte(code_page, 0, wide.c_str(), -1, + WriteInto(&mb, charcount), charcount, NULL, NULL); + + return mb; +} + +// Grabbed from base/string_util.cc +std::string WideToUTF8(const std::wstring& wide) { + return WideToMultiByte(wide, CP_UTF8); +} + +} // namespace + +namespace sandbox { + +// Performs the interception of NtMapViewOfSection on the 64-bit version of +// ntdll.dll. 'thunk' is the buffer on the address space of process 'child', +// that will be used to store the information about the patch. +int PatchNtdll(HANDLE child, void* thunk, size_t thunk_bytes) { + wchar_t* ntdll_name = L"ntdll.dll"; + HMODULE ntdll_base = ::GetModuleHandle(ntdll_name); + if (!ntdll_base) + return 100; + + Service64ResolverThunk resolver(child); + size_t used = resolver.GetThunkSize(); + char* code = reinterpret_cast(thunk) + used; + NTSTATUS ret = resolver.Setup(ntdll_base, NULL, "NtMapViewOfSection", NULL, + code, thunk, thunk_bytes, NULL); + if (!NT_SUCCESS(ret)) + return 101; + + size_t size = reinterpret_cast(&TargetEnd) - + reinterpret_cast(&TargetNtMapViewOfSection); + + if (size + used > thunk_bytes) + return 102; + + SIZE_T written; + if (!::WriteProcessMemory(child, code, &TargetNtMapViewOfSection, size, + &written)) + return 103; + + if (size != written) + return 104; + + return 0; +} + +} // namespace sandbox + +// We must receive two arguments: the process id of the target to intercept and +// the address of a page of memory on that process that will be used for the +// interception. We receive the address because the broker will cleanup the +// patch when the work is performed. +// +// It should be noted that we don't wait until the real work is done; this +// program quits as soon as the 64-bit interception is performed. +int wWinMain(HINSTANCE, HINSTANCE, wchar_t* command_line, int) { + COMPILE_ASSERT(sizeof(void*) > sizeof(DWORD), unsupported_32_bits); + if (!command_line) + return 1; + + wchar_t* next; + DWORD process_id = wcstoul(command_line, &next, 0); + if (!process_id) + return 2; + + DWORD access = PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE; + HANDLE child = ::OpenProcess(access, FALSE, process_id); + if (!child) + return 3; + + DWORD buffer = wcstoul(next, NULL, 0); + if (!buffer) + return 4; + + void* thunk = reinterpret_cast(static_cast(buffer)); + + const size_t kPageSize = 4096; + return sandbox::PatchNtdll(child, thunk, kPageSize); +} diff --git a/sandbox/win/wow_helper/wow_helper.exe b/sandbox/win/wow_helper/wow_helper.exe new file mode 100755 index 0000000..f9bfb4b Binary files /dev/null and b/sandbox/win/wow_helper/wow_helper.exe differ diff --git a/sandbox/win/wow_helper/wow_helper.pdb b/sandbox/win/wow_helper/wow_helper.pdb new file mode 100644 index 0000000..9cb67d0 Binary files /dev/null and b/sandbox/win/wow_helper/wow_helper.pdb differ diff --git a/sandbox/win/wow_helper/wow_helper.vcproj b/sandbox/win/wow_helper/wow_helper.vcproj new file mode 100644 index 0000000..5482fbd --- /dev/null +++ b/sandbox/win/wow_helper/wow_helper.vcproj @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sandbox/wow_helper.sln b/sandbox/wow_helper.sln deleted file mode 100644 index 26d0da2..0000000 --- a/sandbox/wow_helper.sln +++ /dev/null @@ -1,19 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wow_helper", "wow_helper\wow_helper.vcproj", "{BCF3A457-39F1-4DAA-9A65-93CFCD559036}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Debug|x64.ActiveCfg = Debug|x64 - {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Debug|x64.Build.0 = Debug|x64 - {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Release|x64.ActiveCfg = Release|x64 - {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/sandbox/wow_helper/service64_resolver.cc b/sandbox/wow_helper/service64_resolver.cc deleted file mode 100644 index d54e0b0..0000000 --- a/sandbox/wow_helper/service64_resolver.cc +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/wow_helper/service64_resolver.h" - -#include "base/memory/scoped_ptr.h" -#include "sandbox/wow_helper/target_code.h" - -namespace { -#pragma pack(push, 1) - -const BYTE kMovEax = 0xB8; -const BYTE kMovEdx = 0xBA; -const USHORT kCallPtrEdx = 0x12FF; -const BYTE kRet = 0xC2; -const BYTE kNop = 0x90; -const USHORT kJmpEdx = 0xE2FF; -const USHORT kXorEcx = 0xC933; -const ULONG kLeaEdx = 0x0424548D; -const ULONG kCallFs1 = 0xC015FF64; -const ULONG kCallFs2Ret = 0xC2000000; -const BYTE kPopEdx = 0x5A; -const BYTE kPushEdx = 0x52; -const BYTE kPush32 = 0x68; - -const ULONG kMmovR10EcxMovEax = 0xB8D18B4C; -const USHORT kSyscall = 0x050F; -const BYTE kRetNp = 0xC3; -const BYTE kPad = 0x66; -const USHORT kNop16 = 0x9066; -const BYTE kRelJmp = 0xE9; - -const ULONG kXorRaxMovEax = 0xB8C03148; -const ULONG kSaveRcx = 0x10488948; -const ULONG kMovRcxRaxJmp = 0xE9C88B48; - -// Service code for 64 bit systems. -struct ServiceEntry { - // this struct contains roughly the following code: - // mov r10,rcx - // mov eax,52h - // syscall - // ret - // xchg ax,ax - // xchg ax,ax - - ULONG mov_r10_ecx_mov_eax; // = 4C 8B D1 B8 - ULONG service_id; - USHORT syscall; // = 0F 05 - BYTE ret; // = C3 - BYTE pad; // = 66 - USHORT xchg_ax_ax1; // = 66 90 - USHORT xchg_ax_ax2; // = 66 90 -}; - -struct Redirected { - // this struct contains roughly the following code: - // jmp relative_32 - // xchg ax,ax // 3 byte nop - - Redirected() { - jmp = kRelJmp; - relative = 0; - pad = kPad; - xchg_ax_ax = kNop16; - }; - BYTE jmp; // = E9 - ULONG relative; - BYTE pad; // = 66 - USHORT xchg_ax_ax; // = 66 90 -}; - -struct InternalThunk { - // this struct contains roughly the following code: - // xor rax,rax - // mov eax, 0x00080000 // Thunk storage. - // mov [rax]PatchInfo.service, rcx // Save first argument. - // mov rcx, rax - // jmp relative_to_interceptor - - InternalThunk() { - xor_rax_mov_eax = kXorRaxMovEax; - patch_info = 0; - save_rcx = kSaveRcx; - mov_rcx_rax_jmp = kMovRcxRaxJmp; - relative = 0; - }; - ULONG xor_rax_mov_eax; // = 48 31 C0 B8 - ULONG patch_info; - ULONG save_rcx; // = 48 89 48 10 - ULONG mov_rcx_rax_jmp; // = 48 8b c8 e9 - ULONG relative; -}; - -struct ServiceFullThunk { - sandbox::PatchInfo patch_info; - ServiceEntry original; - InternalThunk internal_thunk; -}; - -#pragma pack(pop) - -// Simple utility function to write to a buffer on the child, if the memery has -// write protection attributes. -// Arguments: -// child_process (in): process to write to. -// address (out): memory position on the child to write to. -// buffer (in): local buffer with the data to write . -// length (in): number of bytes to write. -// Returns true on success. -bool WriteProtectedChildMemory(HANDLE child_process, - void* address, - const void* buffer, - size_t length) { - // first, remove the protections - DWORD old_protection; - if (!::VirtualProtectEx(child_process, address, length, - PAGE_WRITECOPY, &old_protection)) - return false; - - SIZE_T written; - bool ok = ::WriteProcessMemory(child_process, address, buffer, length, - &written) && (length == written); - - // always attempt to restore the original protection - if (!::VirtualProtectEx(child_process, address, length, - old_protection, &old_protection)) - return false; - - return ok; -} - -// Get pointers to the functions that we need from ntdll.dll. -NTSTATUS ResolveNtdll(sandbox::PatchInfo* patch_info) { - wchar_t* ntdll_name = L"ntdll.dll"; - HMODULE ntdll = ::GetModuleHandle(ntdll_name); - if (!ntdll) - return STATUS_PROCEDURE_NOT_FOUND; - - void* signal = ::GetProcAddress(ntdll, "NtSignalAndWaitForSingleObject"); - if (!signal) - return STATUS_PROCEDURE_NOT_FOUND; - - patch_info->signal_and_wait = - reinterpret_cast(signal); - - return STATUS_SUCCESS; -} - -}; // namespace - -namespace sandbox { - -NTSTATUS ResolverThunk::Init(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes) { - if (NULL == thunk_storage || 0 == storage_bytes || - NULL == target_module || NULL == target_name) - return STATUS_INVALID_PARAMETER; - - if (storage_bytes < GetThunkSize()) - return STATUS_BUFFER_TOO_SMALL; - - NTSTATUS ret = STATUS_SUCCESS; - if (NULL == interceptor_entry_point) { - ret = ResolveInterceptor(interceptor_module, interceptor_name, - &interceptor_entry_point); - if (!NT_SUCCESS(ret)) - return ret; - } - - ret = ResolveTarget(target_module, target_name, &target_); - if (!NT_SUCCESS(ret)) - return ret; - - interceptor_ = interceptor_entry_point; - - return ret; -} - -NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module, - const char* interceptor_name, - const void** address) { - return STATUS_NOT_IMPLEMENTED; -} - -NTSTATUS ResolverThunk::ResolveTarget(const void* module, - const char* function_name, - void** address) { - return STATUS_NOT_IMPLEMENTED; -} - -NTSTATUS Service64ResolverThunk::Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) { - NTSTATUS ret = Init(target_module, interceptor_module, target_name, - interceptor_name, interceptor_entry_point, - thunk_storage, storage_bytes); - if (!NT_SUCCESS(ret)) - return ret; - - size_t thunk_bytes = GetThunkSize(); - scoped_array thunk_buffer(new char[thunk_bytes]); - ServiceFullThunk* thunk = reinterpret_cast( - thunk_buffer.get()); - - if (!IsFunctionAService(&thunk->original)) - return STATUS_UNSUCCESSFUL; - - ret = PerformPatch(thunk, thunk_storage); - - if (NULL != storage_used) - *storage_used = thunk_bytes; - - return ret; -} - -NTSTATUS Service64ResolverThunk::ResolveInterceptor( - const void* interceptor_module, - const char* interceptor_name, - const void** address) { - // After all, we are using a locally mapped version of the exe, so the - // action is the same as for a target function. - return ResolveTarget(interceptor_module, interceptor_name, - const_cast(address)); -} - -// In this case all the work is done from the parent, so resolve is -// just a simple GetProcAddress. -NTSTATUS Service64ResolverThunk::ResolveTarget(const void* module, - const char* function_name, - void** address) { - if (NULL == module) - return STATUS_UNSUCCESSFUL; - - *address = ::GetProcAddress(bit_cast(module), function_name); - - if (NULL == *address) - return STATUS_UNSUCCESSFUL; - - return STATUS_SUCCESS; -} - -size_t Service64ResolverThunk::GetThunkSize() const { - return sizeof(ServiceFullThunk); -} - -bool Service64ResolverThunk::IsFunctionAService(void* local_thunk) const { - ServiceEntry function_code; - SIZE_T read; - if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) - return false; - - if (sizeof(function_code) != read) - return false; - - if (kMmovR10EcxMovEax != function_code.mov_r10_ecx_mov_eax || - kSyscall != function_code.syscall || kRetNp != function_code.ret) - return false; - - // Save the verified code - memcpy(local_thunk, &function_code, sizeof(function_code)); - - return true; -} - -NTSTATUS Service64ResolverThunk::PerformPatch(void* local_thunk, - void* remote_thunk) { - ServiceFullThunk* full_local_thunk = reinterpret_cast( - local_thunk); - ServiceFullThunk* full_remote_thunk = reinterpret_cast( - remote_thunk); - - // If the source or target are above 4GB we cannot do this relative jump. - if (reinterpret_cast(full_remote_thunk) > - static_cast(ULONG_MAX)) - return STATUS_CONFLICTING_ADDRESSES; - - if (reinterpret_cast(target_) > static_cast(ULONG_MAX)) - return STATUS_CONFLICTING_ADDRESSES; - - // Patch the original code. - Redirected local_service; - Redirected* remote_service = reinterpret_cast(target_); - ULONG_PTR diff = reinterpret_cast(&full_remote_thunk->internal_thunk) - - &remote_service->pad; - local_service.relative = static_cast(diff); - - // Setup the PatchInfo structure. - SIZE_T actual; - if (!::ReadProcessMemory(process_, remote_thunk, local_thunk, - sizeof(PatchInfo), &actual)) - return STATUS_UNSUCCESSFUL; - if (sizeof(PatchInfo) != actual) - return STATUS_UNSUCCESSFUL; - - full_local_thunk->patch_info.orig_MapViewOfSection = reinterpret_cast< - NtMapViewOfSectionFunction>(&full_remote_thunk->original); - full_local_thunk->patch_info.patch_location = target_; - NTSTATUS ret = ResolveNtdll(&full_local_thunk->patch_info); - if (!NT_SUCCESS(ret)) - return ret; - - // Setup the thunk. The jump out is performed from right after the end of the - // thunk (full_remote_thunk + 1). - InternalThunk my_thunk; - ULONG_PTR patch_info = reinterpret_cast(remote_thunk); - my_thunk.patch_info = static_cast(patch_info); - diff = reinterpret_cast(interceptor_) - - reinterpret_cast(full_remote_thunk + 1); - my_thunk.relative = static_cast(diff); - - memcpy(&full_local_thunk->internal_thunk, &my_thunk, sizeof(my_thunk)); - - // copy the local thunk buffer to the child - if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, - sizeof(ServiceFullThunk), &actual)) - return STATUS_UNSUCCESSFUL; - - if (sizeof(ServiceFullThunk) != actual) - return STATUS_UNSUCCESSFUL; - - // and now change the function to intercept, on the child - if (!::WriteProtectedChildMemory(process_, target_, &local_service, - sizeof(local_service))) - return STATUS_UNSUCCESSFUL; - - return STATUS_SUCCESS; -} - -} // namespace sandbox diff --git a/sandbox/wow_helper/service64_resolver.h b/sandbox/wow_helper/service64_resolver.h deleted file mode 100644 index f782172..0000000 --- a/sandbox/wow_helper/service64_resolver.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__ -#define SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__ - -#include "sandbox/src/nt_internals.h" -#include "sandbox/src/resolver.h" - -namespace sandbox { - -// This is the concrete resolver used to perform service-call type functions -// inside ntdll.dll (64-bit). -class Service64ResolverThunk : public ResolverThunk { - public: - // The service resolver needs a child process to write to. - explicit Service64ResolverThunk(HANDLE process) - : process_(process), ntdll_base_(NULL) {} - virtual ~Service64ResolverThunk() {} - - // Implementation of Resolver::Setup. - virtual NTSTATUS Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used); - - // Implementation of Resolver::ResolveInterceptor. - virtual NTSTATUS ResolveInterceptor(const void* module, - const char* function_name, - const void** address); - - // Implementation of Resolver::ResolveTarget. - virtual NTSTATUS ResolveTarget(const void* module, - const char* function_name, - void** address); - - // Implementation of Resolver::GetThunkSize. - virtual size_t GetThunkSize() const; - - protected: - // The unit test will use this member to allow local patch on a buffer. - HMODULE ntdll_base_; - - // Handle of the child process. - HANDLE process_; - - private: - // Returns true if the code pointer by target_ corresponds to the expected - // type of function. Saves that code on the first part of the thunk pointed - // by local_thunk (should be directly accessible from the parent). - virtual bool IsFunctionAService(void* local_thunk) const; - - // Performs the actual patch of target_. - // local_thunk must be already fully initialized, and the first part must - // contain the original code. The real type of this buffer is ServiceFullThunk - // (yes, private). remote_thunk (real type ServiceFullThunk), must be - // allocated on the child, and will contain the thunk data, after this call. - // Returns the apropriate status code. - virtual NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk); - - DISALLOW_COPY_AND_ASSIGN(Service64ResolverThunk); -}; - -} // namespace sandbox - - -#endif // SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__ diff --git a/sandbox/wow_helper/target_code.cc b/sandbox/wow_helper/target_code.cc deleted file mode 100644 index 1f93988..0000000 --- a/sandbox/wow_helper/target_code.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/wow_helper/target_code.h" - -namespace sandbox { - -// Hooks NtMapViewOfSection to detect the load of dlls. -NTSTATUS WINAPI TargetNtMapViewOfSection( - PatchInfo *patch_info, HANDLE process, PVOID *base, ULONG_PTR zero_bits, - SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size, - SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect) { - NTSTATUS ret = patch_info->orig_MapViewOfSection(patch_info->section, process, - base, zero_bits, commit_size, - offset, view_size, inherit, - allocation_type, protect); - - LARGE_INTEGER timeout; - timeout.QuadPart = -(5 * 10000000); // 5 seconds. - - // The wait is alertable. - patch_info->signal_and_wait(patch_info->dll_load, patch_info->continue_load, - TRUE, &timeout); - - return ret; -} - -// Marks the end of the code to copy to the target process. -NTSTATUS WINAPI TargetEnd() { - return STATUS_SUCCESS; -} - -} // namespace sandbox diff --git a/sandbox/wow_helper/target_code.h b/sandbox/wow_helper/target_code.h deleted file mode 100644 index 56db6fe..0000000 --- a/sandbox/wow_helper/target_code.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_WOW_HELPER_TARGET_CODE_H__ -#define SANDBOX_WOW_HELPER_TARGET_CODE_H__ - -#include "sandbox/src/nt_internals.h" - -namespace sandbox { - -extern "C" { - -// Holds the information needed for the interception of NtMapViewOfSection. -// Changes of this structure must be synchronized with changes of PatchInfo32 -// on sandbox/src/wow64.cc. -struct PatchInfo { - HANDLE dll_load; // Event to signal the broker. - HANDLE continue_load; // Event to wait for the broker. - HANDLE section; // First argument of the call. - NtMapViewOfSectionFunction orig_MapViewOfSection; - NtSignalAndWaitForSingleObjectFunction signal_and_wait; - void* patch_location; -}; - -// Interception of NtMapViewOfSection on the child process. -// It should never be called directly. This function provides the means to -// detect dlls being loaded, so we can patch them if needed. -NTSTATUS WINAPI TargetNtMapViewOfSection( - PatchInfo* patch_info, HANDLE process, PVOID* base, ULONG_PTR zero_bits, - SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size, - SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect); - -// Marker of the end of TargetNtMapViewOfSection. -NTSTATUS WINAPI TargetEnd(); - -} // extern "C" - -} // namespace sandbox - -#endif // SANDBOX_WOW_HELPER_TARGET_CODE_H__ diff --git a/sandbox/wow_helper/wow_helper.cc b/sandbox/wow_helper/wow_helper.cc deleted file mode 100644 index 6c272e4..0000000 --- a/sandbox/wow_helper/wow_helper.cc +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Wow_helper.exe is a simple Win32 64-bit executable designed to help to -// sandbox a 32 bit application running on a 64 bit OS. The basic idea is to -// perform a 64 bit interception of the target process and notify the 32-bit -// broker process whenever a DLL is being loaded. This allows the broker to -// setup the interceptions (32-bit) properly on the target. - -#include - -#include - -#include "sandbox/wow_helper/service64_resolver.h" -#include "sandbox/wow_helper/target_code.h" - -namespace { - -// Grabbed from base/string_util.h -template -inline typename string_type::value_type* WriteInto(string_type* str, - size_t length_with_null) { - str->reserve(length_with_null); - str->resize(length_with_null - 1); - return &((*str)[0]); -} - -// Grabbed from base/string_util.cc -std::string WideToMultiByte(const std::wstring& wide, UINT code_page) { - if (wide.length() == 0) - return std::string(); - - // compute the length of the buffer we'll need - int charcount = WideCharToMultiByte(code_page, 0, wide.c_str(), -1, - NULL, 0, NULL, NULL); - if (charcount == 0) - return std::string(); - - // convert - std::string mb; - WideCharToMultiByte(code_page, 0, wide.c_str(), -1, - WriteInto(&mb, charcount), charcount, NULL, NULL); - - return mb; -} - -// Grabbed from base/string_util.cc -std::string WideToUTF8(const std::wstring& wide) { - return WideToMultiByte(wide, CP_UTF8); -} - -} // namespace - -namespace sandbox { - -// Performs the interception of NtMapViewOfSection on the 64-bit version of -// ntdll.dll. 'thunk' is the buffer on the address space of process 'child', -// that will be used to store the information about the patch. -int PatchNtdll(HANDLE child, void* thunk, size_t thunk_bytes) { - wchar_t* ntdll_name = L"ntdll.dll"; - HMODULE ntdll_base = ::GetModuleHandle(ntdll_name); - if (!ntdll_base) - return 100; - - Service64ResolverThunk resolver(child); - size_t used = resolver.GetThunkSize(); - char* code = reinterpret_cast(thunk) + used; - NTSTATUS ret = resolver.Setup(ntdll_base, NULL, "NtMapViewOfSection", NULL, - code, thunk, thunk_bytes, NULL); - if (!NT_SUCCESS(ret)) - return 101; - - size_t size = reinterpret_cast(&TargetEnd) - - reinterpret_cast(&TargetNtMapViewOfSection); - - if (size + used > thunk_bytes) - return 102; - - SIZE_T written; - if (!::WriteProcessMemory(child, code, &TargetNtMapViewOfSection, size, - &written)) - return 103; - - if (size != written) - return 104; - - return 0; -} - -} // namespace sandbox - -// We must receive two arguments: the process id of the target to intercept and -// the address of a page of memory on that process that will be used for the -// interception. We receive the address because the broker will cleanup the -// patch when the work is performed. -// -// It should be noted that we don't wait until the real work is done; this -// program quits as soon as the 64-bit interception is performed. -int wWinMain(HINSTANCE, HINSTANCE, wchar_t* command_line, int) { - COMPILE_ASSERT(sizeof(void*) > sizeof(DWORD), unsupported_32_bits); - if (!command_line) - return 1; - - wchar_t* next; - DWORD process_id = wcstoul(command_line, &next, 0); - if (!process_id) - return 2; - - DWORD access = PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE; - HANDLE child = ::OpenProcess(access, FALSE, process_id); - if (!child) - return 3; - - DWORD buffer = wcstoul(next, NULL, 0); - if (!buffer) - return 4; - - void* thunk = reinterpret_cast(static_cast(buffer)); - - const size_t kPageSize = 4096; - return sandbox::PatchNtdll(child, thunk, kPageSize); -} diff --git a/sandbox/wow_helper/wow_helper.exe b/sandbox/wow_helper/wow_helper.exe deleted file mode 100755 index f9bfb4b..0000000 Binary files a/sandbox/wow_helper/wow_helper.exe and /dev/null differ diff --git a/sandbox/wow_helper/wow_helper.pdb b/sandbox/wow_helper/wow_helper.pdb deleted file mode 100644 index 9cb67d0..0000000 Binary files a/sandbox/wow_helper/wow_helper.pdb and /dev/null differ diff --git a/sandbox/wow_helper/wow_helper.vcproj b/sandbox/wow_helper/wow_helper.vcproj deleted file mode 100644 index 5482fbd..0000000 --- a/sandbox/wow_helper/wow_helper.vcproj +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- cgit v1.1