diff --git a/CMakeLists.txt b/CMakeLists.txt index 6518f703..9f857c04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,13 @@ option(MP_BUILD_VSPACKAGE "Build Visual Studio Package." ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/build.props) +include(architecture) include(unipath) +set(injector "micro-profiler_injector_${archid}") +set(micro-profiler "micro-profiler_${archid}") +set(sandbox "micro-profiler_sandbox_${archid}") + enable_testing() @@ -149,6 +154,7 @@ if (WIN32 OR APPLE) if (WIN32) add_subdirectory(explorer/src) add_subdirectory(injector/src) + add_subdirectory(sandbox/src) if (MP_BUILD_VSPACKAGE) add_subdirectory(libraries/wpl.vs) diff --git a/build-vsix.cmd b/build-vsix.cmd index 50371564..bf4e317b 100644 --- a/build-vsix.cmd +++ b/build-vsix.cmd @@ -26,10 +26,16 @@ pushd "%~dp0_build.windows.x86\_bin" call sha256 hashmpx86 micro-profiler_Win32.dll call sha256 hashmpx86lib micro-profiler_Win32.lib + + call sha256 hashsbxx86 micro-profiler_sandbox_Win32.exe + call sha256 hashinjx86 micro-profiler_injector_Win32.dll popd pushd "%~dp0_build.windows.x64\_bin" call sha256 hashmpx64 micro-profiler_x64.dll call sha256 hashmpx64lib micro-profiler_x64.lib + + call sha256 hashsbxx64 micro-profiler_sandbox_x64.exe + call sha256 hashinjx64 micro-profiler_injector_x64.dll popd pushd "%~dp0_build.linux.x86\_bin" call sha256 hashlinuxmpx86 libmicro-profiler_x86.so @@ -68,10 +74,16 @@ pushd "%~dp0_build.windows.x86\_bin" call mkzip micro-profiler_Win32.dll "%OUTPUT%" call mkzip micro-profiler_Win32.lib "%OUTPUT%" + + call mkzip micro-profiler_sandbox_Win32.exe "%OUTPUT%" + call mkzip micro-profiler_injector_Win32.dll "%OUTPUT%" popd pushd "%~dp0_build.windows.x64\_bin" call mkzip micro-profiler_x64.dll "%OUTPUT%" call mkzip micro-profiler_x64.lib "%OUTPUT%" + + call mkzip micro-profiler_sandbox_x64.exe "%OUTPUT%" + call mkzip micro-profiler_injector_x64.dll "%OUTPUT%" popd pushd "%~dp0_build.linux.x86\_bin" call mkzip libmicro-profiler_x86.so "%OUTPUT%" diff --git a/build-vsix.x64.cmd b/build-vsix.x64.cmd index ef73b045..0675687c 100644 --- a/build-vsix.x64.cmd +++ b/build-vsix.x64.cmd @@ -19,6 +19,9 @@ popd pushd "%~dp0_build.windows.x86\_bin" call sha256 hashmpx86 micro-profiler_Win32.dll call sha256 hashmpx86lib micro-profiler_Win32.lib + + call sha256 hashsbxx86 micro-profiler_sandbox_Win32.exe + call sha256 hashinjx86 micro-profiler_injector_Win32.dll popd pushd "%~dp0_build.windows.x64\_bin" copy /y extension.x64.vsixmanifest extension.vsixmanifest @@ -32,6 +35,9 @@ pushd "%~dp0_build.windows.x64\_bin" call sha256 hashmpx64 micro-profiler_x64.dll call sha256 hashmpx64lib micro-profiler_x64.lib + + call sha256 hashsbxx64 micro-profiler_sandbox_x64.exe + call sha256 hashinjx64 micro-profiler_injector_x64.dll popd pushd "%~dp0_build.linux.x86\_bin" call sha256 hashlinuxmpx86 libmicro-profiler_x86.so @@ -63,6 +69,8 @@ popd pushd "%~dp0_build.windows.x86\_bin" call mkzip micro-profiler_Win32.dll "%OUTPUT%" call mkzip micro-profiler_Win32.lib "%OUTPUT%" + call mkzip micro-profiler_sandbox_Win32.exe "%OUTPUT%" + call mkzip micro-profiler_injector_Win32.dll "%OUTPUT%" popd pushd "%~dp0_build.windows.x64\_bin" call mkzip micro-profiler_frontend.dll "%OUTPUT%" @@ -74,6 +82,8 @@ pushd "%~dp0_build.windows.x64\_bin" call mkzip micro-profiler_x64.dll "%OUTPUT%" call mkzip micro-profiler_x64.lib "%OUTPUT%" + call mkzip micro-profiler_sandbox_x64.exe "%OUTPUT%" + call mkzip micro-profiler_injector_x64.dll "%OUTPUT%" popd pushd "%~dp0_build.linux.x86\_bin" call mkzip libmicro-profiler_x86.so "%OUTPUT%" diff --git a/build.props/architecture.cmake b/build.props/architecture.cmake new file mode 100644 index 00000000..3699cab5 --- /dev/null +++ b/build.props/architecture.cmake @@ -0,0 +1,13 @@ +if(NOT ANDROID_ABI OR ANDROID_ABI MATCHES "x86.*") + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + if(WIN32) + set(archid "Win32") + else() + set(archid "x86") + endif() + else() + set(archid "x64") + endif() +elseif(ANDROID_ABI MATCHES "arm.*") + set(archid "arm") +endif() diff --git a/collector/src/CMakeLists.txt b/collector/src/CMakeLists.txt index 1c2ae745..1b5b766a 100644 --- a/collector/src/CMakeLists.txt +++ b/collector/src/CMakeLists.txt @@ -1,19 +1,5 @@ cmake_minimum_required(VERSION 3.4) -if(NOT ANDROID_ABI OR ANDROID_ABI MATCHES "x86.*") - if(CMAKE_SIZEOF_VOID_P EQUAL 4) - if(WIN32) - set(micro-profiler "micro-profiler_Win32") - else() - set(micro-profiler "micro-profiler_x86") - endif() - else() - set(micro-profiler "micro-profiler_x64") - endif() -elseif(ANDROID_ABI MATCHES "arm.*") - set(micro-profiler "micro-profiler_arm") -endif() - set(MP_OUTDIR $) if(MSVC) diff --git a/deployment/vsix/[Content_Types].xml b/deployment/vsix/[Content_Types].xml index f86dcda6..30b707cf 100644 --- a/deployment/vsix/[Content_Types].xml +++ b/deployment/vsix/[Content_Types].xml @@ -2,6 +2,7 @@ + diff --git a/deployment/vsix/manifest.json b/deployment/vsix/manifest.json index c8ba8a55..2e48b9bb 100644 --- a/deployment/vsix/manifest.json +++ b/deployment/vsix/manifest.json @@ -20,6 +20,11 @@ {"fileName":"/libmicro-profiler_x86.so","sha256":"!hashlinuxmpx86!"}, {"fileName":"/libmicro-profiler_x64.so","sha256":"!hashlinuxmpx64!"}, + {"fileName":"/micro-profiler_sandbox_Win32.exe","sha256":"!hashsbxx86!"}, + {"fileName":"/micro-profiler_sandbox_x64.exe","sha256":"!hashsbxx64!"}, + {"fileName":"/micro-profiler_injector_Win32.dll","sha256":"!hashinjx86!"}, + {"fileName":"/micro-profiler_injector_x64.dll","sha256":"!hashinjx64!"}, + {"fileName":"/windows7+/x64/api-ms-win-downlevel-kernel32-l2-1-0.dll","sha256":"!hashkernel32downlevelapix64!"}, {"fileName":"/windows7+/x64/dbghelp.dll","sha256":"!hashdbghelpx64!"}, {"fileName":"/windows7+/x86/api-ms-win-downlevel-kernel32-l2-1-0.dll","sha256":"!hashkernel32downlevelapix86!"}, diff --git a/deployment/wix/micro-profiler.wxs b/deployment/wix/micro-profiler.wxs index f7a3dc89..bbc6703f 100644 --- a/deployment/wix/micro-profiler.wxs +++ b/deployment/wix/micro-profiler.wxs @@ -142,8 +142,12 @@ + + + + diff --git a/explorer/src/process_win32.cpp b/explorer/src/process_win32.cpp index c7121075..ddb78c9c 100644 --- a/explorer/src/process_win32.cpp +++ b/explorer/src/process_win32.cpp @@ -66,6 +66,12 @@ namespace micro_profiler { if (auto handle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, entry.th32ProcessID)) { + BOOL wow64 = FALSE; + + // TODO: untested + if (::IsWow64Process(handle, &wow64)) + p.architecture = wow64 ? process_info::x86 : process_info::x64; + p.handle.reset(handle, &::CloseHandle); } else diff --git a/injector/injector.h b/injector/injector.h new file mode 100644 index 00000000..9ada4442 --- /dev/null +++ b/injector/injector.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011-2022 by Artem A. Gevorkyan (gevorkyan.org) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once + +#include + +namespace micro_profiler +{ + enum injector_messages_id { + request_injection, + response_injected, + }; + + struct injection_info + { + std::string collector_path; + std::string frontend_endpoint_id; + unsigned int pid; + }; + + + + template + inline void serialize(ArchiveT &archive, injection_info &info) + { + archive(info.collector_path); + archive(info.frontend_endpoint_id); + archive(info.pid); + } + + template + inline void serialize(ArchiveT &archive, injector_messages_id &data) + { archive(reinterpret_cast(data)); } +} diff --git a/injector/process.h b/injector/process.h index 55ac714c..979227db 100644 --- a/injector/process.h +++ b/injector/process.h @@ -20,20 +20,28 @@ #pragma once +#include #include #include #include namespace micro_profiler { - struct process + class process : noncopyable { + public: typedef void (injection_function_t)(const_byte_range payload); - virtual unsigned get_pid() const = 0; - virtual std::string name() const = 0; - virtual void remote_execute(injection_function_t *injection_function, const_byte_range payload) = 0; + public: + process(unsigned int pid); + ~process(); - static std::shared_ptr open(unsigned int pid); + void remote_execute(injection_function_t *injection_function, const_byte_range payload); + + private: + class impl; + + private: + impl *_impl; }; } diff --git a/injector/src/CMakeLists.txt b/injector/src/CMakeLists.txt index 6ab94a8c..1784a3a2 100644 --- a/injector/src/CMakeLists.txt +++ b/injector/src/CMakeLists.txt @@ -1,9 +1,16 @@ cmake_minimum_required(VERSION 2.8) -set(INJECTOR_SOURCES +add_definitions(-DMP_EXPORT_PREFIX=__declspec\(dllexport\)) + +set(INJECTOR_LIB_SOURCES process_win32.cpp ) +set(INJECTOR_SOURCES + injector.cpp +) -add_library(injector STATIC ${INJECTOR_SOURCES}) +add_library(injector.lib STATIC ${INJECTOR_LIB_SOURCES}) +add_library(${injector} SHARED ${INJECTOR_SOURCES}) -target_link_libraries(injector psapi.lib) +target_link_libraries(injector.lib psapi.lib) +target_link_libraries(${injector} injector.lib ipc common logger scheduler) diff --git a/micro-profiler/frontend/inject_profiler.cpp b/injector/src/injector.cpp similarity index 55% rename from micro-profiler/frontend/inject_profiler.cpp rename to injector/src/injector.cpp index 8a903e4a..c922b66a 100644 --- a/micro-profiler/frontend/inject_profiler.cpp +++ b/injector/src/injector.cpp @@ -18,68 +18,53 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include #include -#include -#include -#include #include #include +#include +#include +#include #include #include -#include +using namespace micro_profiler; +using namespace micro_profiler::ipc; using namespace std; extern "C" int setenv(const char *name, const char *value, int overwrite); -namespace micro_profiler +namespace { - namespace + void inject_profiler_worker(const_byte_range payload) { -#ifdef _M_IX86 - const string c_profiler_module_name = "micro-profiler_Win32.dll"; -#elif _M_X64 - const string c_profiler_module_name = "micro-profiler_x64.dll"; -#endif + buffer_reader r(payload); + strmd::deserializer d(r); + injection_info injection; - struct parameters - { - string frontend_id; - }; - - template - void serialize(ArchiveT &archive, parameters &data) - { - archive(data.frontend_id); - } - - void inject_profiler_worker(const_byte_range payload) - { - buffer_reader r(payload); - strmd::deserializer d(r); - parameters p; - const auto dir = ~get_module_info(&inject_profiler).path; - - d(p); - setenv(constants::frontend_id_ev, p.frontend_id.c_str(), 1); - setenv(constants::profiler_injected_ev, "1", 1); - load_library(dir & c_profiler_module_name); // No need to hold the handle, as profiler pins to the process. - } + d(injection); + setenv(constants::frontend_id_ev, injection.frontend_endpoint_id.c_str(), 1); + setenv(constants::profiler_injected_ev, "1", 1); + load_library(injection.collector_path); // No need to hold the handle, as profiler pins to the process. } +} - void inject_profiler(process &process_, const std::string &frontend_id) - { +extern "C" MP_EXPORT_PREFIX void ipc_spawn_server(micro_profiler::ipc::channel_ptr_t &session_, + const vector &/*arguments*/, micro_profiler::ipc::channel &outbound) +{ + const auto session = make_shared(outbound); + + session->add_handler(request_injection, [] (server_session::response &response, const injection_info &injection) { + auto p = make_shared(injection.pid); pod_vector buffer; buffer_writer< pod_vector > w(buffer); strmd::serializer >, strmd::varint> s(w); - parameters p = { - frontend_id, - }; - s(p); - process_.remote_execute(&inject_profiler_worker, const_byte_range(buffer.data(), buffer.size())); - } + s(injection); + p->remote_execute(&inject_profiler_worker, const_byte_range(buffer.data(), buffer.size())); + response(response_injected); + }); + session_ = session; } diff --git a/injector/src/injector.vcxproj b/injector/src/injector.vcxproj new file mode 100644 index 00000000..4efdf4b1 --- /dev/null +++ b/injector/src/injector.vcxproj @@ -0,0 +1,73 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {6E5EE908-90AE-46EA-A9FA-506DF8C2F89A} + + + + DynamicLibrary + + + + + + + + micro-profiler_$(ProjectName)_$(Platform) + + + + %(AdditionalIncludeDirectories) + false + MP_EXPORT_PREFIX=__declspec(dllexport);%(PreprocessorDefinitions) + + + true + + + + + + + + + + + {69508827-452f-479e-a28f-af300c5c1633} + + + {416ee095-ea66-4fa4-be02-0ce790766b25} + + + {2ecfc1ae-8829-4a91-9b6e-2befc569acf7} + + + {3d321437-3220-4baf-aa87-a5d6297bbe82} + + + {d319214f-4c16-406a-9ad5-70d1b4f4aa4e} + + + {b0596b9d-8303-4fd1-8a0a-5db7882f6f89} + + + + \ No newline at end of file diff --git a/injector/src/injector.vcxproj.filters b/injector/src/injector.vcxproj.filters new file mode 100644 index 00000000..41a07709 --- /dev/null +++ b/injector/src/injector.vcxproj.filters @@ -0,0 +1,16 @@ + + + + + {ac610505-74b8-4980-b145-07995ffad8b6} + + + + + src + + + + + + \ No newline at end of file diff --git a/injector/src/process_win32.cpp b/injector/src/process_win32.cpp index 63323f1a..1e31dadc 100644 --- a/injector/src/process_win32.cpp +++ b/injector/src/process_win32.cpp @@ -31,116 +31,122 @@ using namespace std; using namespace std::placeholders; +extern "C" int setenv(const char *name, const char *value, int overwrite) +{ + if (overwrite || !GetEnvironmentVariableA(name, NULL, 0)) + ::SetEnvironmentVariableA(name, value); + return 0; +} + namespace micro_profiler { - namespace win32 + class process::impl { - int g_dummy; - - class process : public micro_profiler::process + public: + impl(unsigned int pid) + : _pid(pid), _hprocess(::OpenProcess(rights, FALSE, pid), &::CloseHandle) { - public: - process(unsigned int pid, const string &name_) - : _pid(pid), _name(name_), _hprocess(::OpenProcess(rights, FALSE, pid), &::CloseHandle) - { - if (!_hprocess) - throw runtime_error(""); - } + if (!_hprocess) + throw runtime_error(""); + } - virtual unsigned get_pid() const - { return _pid; } + void remote_execute(injection_function_t *injection, const_byte_range payload) + { + const mapped_module m = get_module_info(&foreign_worker); + const shared_ptr fpath = foreign_allocate(m.path.size() + 1); + const size_t injection_offset = (byte *)injection - m.base; + const size_t payload_size = payload.length(); + const shared_ptr fpayload = foreign_allocate(sizeof(injection_offset) + sizeof(payload_size) + + payload.length()); + const auto hkernel = load_library("kernel32"); + + ::WriteProcessMemory(_hprocess.get(), fpath.get(), m.path.c_str(), m.path.size() + 1, NULL); + foreign_execute((PTHREAD_START_ROUTINE)::GetProcAddress(static_cast(hkernel.get()), "LoadLibraryA"), + fpath.get()); + + auto fbase = static_cast(find_loaded_module(m.path)); + + ::WriteProcessMemory(_hprocess.get(), fpayload.get(), &injection_offset, sizeof(injection_offset), + NULL); + ::WriteProcessMemory(_hprocess.get(), fpayload.get() + sizeof(injection_offset), + &payload_size, sizeof(payload_size), NULL); + ::WriteProcessMemory(_hprocess.get(), fpayload.get() + sizeof(injection_offset) + sizeof(payload_size), + payload.begin(), payload.length(), NULL); + foreign_execute((PTHREAD_START_ROUTINE)(fbase + ((byte *)&foreign_worker - m.base)), fpayload.get()); + + // TODO: untested + foreign_execute((PTHREAD_START_ROUTINE)::GetProcAddress(static_cast(hkernel.get()), "FreeLibrary"), + fbase); + } + + private: + enum { + rights = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE + | PROCESS_VM_READ, + }; - virtual string name() const - { return _name; } + private: + void *foreign_execute(PTHREAD_START_ROUTINE thread_routine, void *data) + { + shared_ptr hthread(::CreateRemoteThread(_hprocess.get(), NULL, 0, thread_routine, data, 0, NULL), + &::CloseHandle); + DWORD exit_code; - virtual void remote_execute(injection_function_t *injection, const_byte_range payload) - { - const mapped_module m = get_module_info(&g_dummy); - const shared_ptr fpath = foreign_allocate(m.path.size() + 1); - const size_t injection_offset = (byte *)injection - m.base; - const size_t payload_size = payload.length(); - const shared_ptr fpayload = foreign_allocate(sizeof(injection_offset) + sizeof(payload_size) - + payload.length()); - const auto hkernel = load_library("kernel32"); - - ::WriteProcessMemory(_hprocess.get(), fpath.get(), m.path.c_str(), m.path.size() + 1, NULL); - foreign_execute((PTHREAD_START_ROUTINE)::GetProcAddress(static_cast(hkernel.get()), "LoadLibraryA"), - fpath.get()); - - auto fbase = static_cast(find_loaded_module(m.path)); - - ::WriteProcessMemory(_hprocess.get(), fpayload.get(), &injection_offset, sizeof(injection_offset), - NULL); - ::WriteProcessMemory(_hprocess.get(), fpayload.get() + sizeof(injection_offset), - &payload_size, sizeof(payload_size), NULL); - ::WriteProcessMemory(_hprocess.get(), fpayload.get() + sizeof(injection_offset) + sizeof(payload_size), - payload.begin(), payload.length(), NULL); - foreign_execute((PTHREAD_START_ROUTINE)(fbase + ((byte *)&foreign_worker - m.base)), fpayload.get()); - } + ::WaitForSingleObject(hthread.get(), INFINITE); + return ::GetExitCodeThread(hthread.get(), &exit_code), + reinterpret_cast(static_cast(exit_code)); + } - private: - enum { - rights = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE - | PROCESS_VM_READ, - }; + shared_ptr foreign_allocate(size_t size) + { + return shared_ptr(static_cast(::VirtualAllocEx(_hprocess.get(), 0, size, MEM_COMMIT, + PAGE_EXECUTE_READWRITE)), bind(&::VirtualFreeEx, _hprocess.get(), _1, 0, MEM_RELEASE)); + } - private: - void *foreign_execute(PTHREAD_START_ROUTINE thread_routine, void *data) - { - shared_ptr hthread(::CreateRemoteThread(_hprocess.get(), NULL, 0, thread_routine, data, 0, NULL), - &::CloseHandle); - DWORD exit_code; + static void __stdcall foreign_worker(void *data_) + { + const auto m = get_module_info(&foreign_worker); + const auto injection_offset = static_cast(data_); + const auto size = injection_offset + 1; + const auto data = reinterpret_cast(size + 1); + const auto injection = reinterpret_cast(m.base + *injection_offset); - ::WaitForSingleObject(hthread.get(), INFINITE); - return ::GetExitCodeThread(hthread.get(), &exit_code), - reinterpret_cast(static_cast(exit_code)); - } + (*injection)(const_byte_range(data, *size)); + }; - shared_ptr foreign_allocate(size_t size) + void *find_loaded_module(const string &path) + { + file_id fid(path); + DWORD needed; + vector buffer; + + if (!::EnumProcessModules(_hprocess.get(), 0, 0, &needed)) + throw runtime_error("Cannot enumerate modules in the target process!"); + buffer.resize(needed / sizeof(HMODULE)); + ::EnumProcessModules(_hprocess.get(), buffer.data(), needed, &needed); + for (auto i = buffer.begin(); i != buffer.end(); ++i) { - return shared_ptr(static_cast(::VirtualAllocEx(_hprocess.get(), 0, size, MEM_COMMIT, - PAGE_EXECUTE_READWRITE)), bind(&::VirtualFreeEx, _hprocess.get(), _1, 0, MEM_RELEASE)); - } + wchar_t path_buffer[MAX_PATH + 1] = { 0 }; - static void __stdcall foreign_worker(void *data_) - { - const mapped_module m = get_module_info(&g_dummy); - size_t *injection_offset = (size_t *)data_; - size_t *size = (size_t *)(injection_offset + 1); - const byte *data = (const byte *)(size + 1); - injection_function_t *injection = (injection_function_t *)(m.base + *injection_offset); + ::GetModuleFileNameExW(_hprocess.get(), *i, path_buffer, sizeof(path_buffer) / sizeof(wchar_t)); + if (file_id(unicode(path_buffer)) == fid) + return *i; + } + return 0; + } - (*injection)(const_byte_range(data, *size)); - }; + private: + unsigned _pid; + shared_ptr _hprocess; + }; - void *find_loaded_module(const string &path) - { - file_id fid(path); - DWORD needed; - vector buffer; - - if (!::EnumProcessModules(_hprocess.get(), 0, 0, &needed)) - throw runtime_error("Cannot enumerate modules in the target process!"); - buffer.resize(needed / sizeof(HMODULE)); - ::EnumProcessModules(_hprocess.get(), buffer.data(), needed, &needed); - for (auto i = buffer.begin(); i != buffer.end(); ++i) - { - wchar_t path_buffer[MAX_PATH + 1] = { 0 }; - - ::GetModuleFileNameExW(_hprocess.get(), *i, path_buffer, sizeof(path_buffer) / sizeof(wchar_t)); - if (file_id(unicode(path_buffer)) == fid) - return *i; - } - return 0; - } + process::process(unsigned int pid) + : _impl(new impl(pid)) + { } - private: - unsigned _pid; - string _name; - shared_ptr _hprocess; - }; - } + process::~process() + { delete _impl; } - shared_ptr process::open(unsigned int pid) - { return shared_ptr(new win32::process(pid, string())); } + void process::remote_execute(injection_function_t *injection_function, const_byte_range payload) + { _impl->remote_execute(injection_function, payload); } } diff --git a/injector/tests/CMakeLists.txt b/injector/tests/CMakeLists.txt index e96a7ea4..aaa01391 100644 --- a/injector/tests/CMakeLists.txt +++ b/injector/tests/CMakeLists.txt @@ -15,5 +15,5 @@ add_custom_command(OUTPUT copy-guineas.x ) add_library(injector.tests SHARED ${INJECTOR_TEST_SOURCES} copy-guineas.x) -target_link_libraries(injector.tests injector ipc logger common mt test-helpers) +target_link_libraries(injector.tests injector.lib ipc logger common mt test-helpers) add_dependencies(injector.tests guinea_runner) diff --git a/injector/tests/ProcessDiscoverTests.cpp b/injector/tests/ProcessDiscoverTests.cpp index 50383276..30203388 100644 --- a/injector/tests/ProcessDiscoverTests.cpp +++ b/injector/tests/ProcessDiscoverTests.cpp @@ -54,7 +54,7 @@ namespace micro_profiler test( MissingProcessCannotBeOpened ) { // ACT / ASSERT - assert_throws(process::open(123123), runtime_error); // the number is quite bug for usual Windows processes + assert_throws(auto p = make_shared(123123), runtime_error); // the number is quite bug for usual Windows processes } @@ -66,7 +66,7 @@ namespace micro_profiler controller->wait_connection(); // INIT / ACT / ASSERT - assert_not_null(process::open(child->get_pid())); + assert_not_null(make_shared(child->get_pid())); } @@ -79,7 +79,7 @@ namespace micro_profiler id.push_back(char()); controller->wait_connection(); - shared_ptr remote = process::open(child->get_pid()); + auto remote = make_shared(child->get_pid()); // ACT remote->remote_execute(&make_connection, mkrange(id)); diff --git a/ipc/src/client_endpoint_spawn_win32.cpp b/ipc/src/client_endpoint_spawn_win32.cpp index c9816e90..2b106442 100644 --- a/ipc/src/client_endpoint_spawn_win32.cpp +++ b/ipc/src/client_endpoint_spawn_win32.cpp @@ -89,7 +89,7 @@ namespace micro_profiler for (auto i = arguments.begin(); i != arguments.end(); ++i) append_quoted(command_line, unicode(*i)); command_line.back() = 0; - if (!::CreateProcessW(NULL, command_line.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &process)) + if (!::CreateProcessW(NULL, command_line.data(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &process)) throw server_exe_not_found(("Server executable not found: " + spawned_path).c_str()); ::CloseHandle(process.hProcess); diff --git a/micro-profiler.sln b/micro-profiler.sln index 398882a7..ea932b93 100644 --- a/micro-profiler.sln +++ b/micro-profiler.sln @@ -167,6 +167,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "guinea_ipc_spawn_server", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ipc.spawn", "ipc\src\ipc.spawn.vcxproj", "{416EE095-EA66-4FA4-BE02-0CE790766B25}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "injector", "injector\src\injector.vcxproj", "{6E5EE908-90AE-46EA-A9FA-506DF8C2F89A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox", "sandbox\src\sandbox.vcxproj", "{03B7EEFD-9214-4B93-8862-15EF9615F862}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -633,6 +637,22 @@ Global {416EE095-EA66-4FA4-BE02-0CE790766B25}.Release|Win32.Build.0 = Release|Win32 {416EE095-EA66-4FA4-BE02-0CE790766B25}.Release|x64.ActiveCfg = Release|x64 {416EE095-EA66-4FA4-BE02-0CE790766B25}.Release|x64.Build.0 = Release|x64 + {6E5EE908-90AE-46EA-A9FA-506DF8C2F89A}.Debug|Win32.ActiveCfg = Debug|Win32 + {6E5EE908-90AE-46EA-A9FA-506DF8C2F89A}.Debug|Win32.Build.0 = Debug|Win32 + {6E5EE908-90AE-46EA-A9FA-506DF8C2F89A}.Debug|x64.ActiveCfg = Debug|x64 + {6E5EE908-90AE-46EA-A9FA-506DF8C2F89A}.Debug|x64.Build.0 = Debug|x64 + {6E5EE908-90AE-46EA-A9FA-506DF8C2F89A}.Release|Win32.ActiveCfg = Release|Win32 + {6E5EE908-90AE-46EA-A9FA-506DF8C2F89A}.Release|Win32.Build.0 = Release|Win32 + {6E5EE908-90AE-46EA-A9FA-506DF8C2F89A}.Release|x64.ActiveCfg = Release|x64 + {6E5EE908-90AE-46EA-A9FA-506DF8C2F89A}.Release|x64.Build.0 = Release|x64 + {03B7EEFD-9214-4B93-8862-15EF9615F862}.Debug|Win32.ActiveCfg = Debug|Win32 + {03B7EEFD-9214-4B93-8862-15EF9615F862}.Debug|Win32.Build.0 = Debug|Win32 + {03B7EEFD-9214-4B93-8862-15EF9615F862}.Debug|x64.ActiveCfg = Debug|x64 + {03B7EEFD-9214-4B93-8862-15EF9615F862}.Debug|x64.Build.0 = Debug|x64 + {03B7EEFD-9214-4B93-8862-15EF9615F862}.Release|Win32.ActiveCfg = Release|Win32 + {03B7EEFD-9214-4B93-8862-15EF9615F862}.Release|Win32.Build.0 = Release|Win32 + {03B7EEFD-9214-4B93-8862-15EF9615F862}.Release|x64.ActiveCfg = Release|x64 + {03B7EEFD-9214-4B93-8862-15EF9615F862}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/micro-profiler/CMakeLists.txt b/micro-profiler/CMakeLists.txt index 11875fc5..af4c701e 100644 --- a/micro-profiler/CMakeLists.txt +++ b/micro-profiler/CMakeLists.txt @@ -40,7 +40,6 @@ add_custom_command(OUTPUT micro-profiler.pkgdef set(MICROPROFILER_SOURCES frontend/attach_ui.cpp - frontend/inject_profiler.cpp module/main.cpp module/micro-profiler.def resources/micro-profiler_ui.rc @@ -73,7 +72,7 @@ add_library(micro-profiler_frontend SHARED ${MICROPROFILER_SOURCES}) add_library(micro-profiler.ui SHARED ${MICROPROFILER_UI_SOURCES}) add_library(micro-profiler.ui.vs10 SHARED ${MICROPROFILER_UI_VS10_SOURCES}) -target_link_libraries(micro-profiler_frontend frontend.lib ipc injector logger scheduler common wpl.vs wpl explorer version) +target_link_libraries(micro-profiler_frontend frontend.lib ipc logger scheduler common wpl.vs wpl explorer version) set_target_properties(micro-profiler.ui micro-profiler.ui.vs10 PROPERTIES LINK_OPTIONS /NOENTRY RUNTIME_OUTPUT_DIRECTORY ${MP_OUTDIR}/1033) foreach(CFG ${CMAKE_CONFIGURATION_TYPES}) diff --git a/micro-profiler/frontend/attach_ui.cpp b/micro-profiler/frontend/attach_ui.cpp index 86d55d42..8bafa619 100644 --- a/micro-profiler/frontend/attach_ui.cpp +++ b/micro-profiler/frontend/attach_ui.cpp @@ -19,17 +19,24 @@ // THE SOFTWARE. #include "attach_ui.h" -#include "inject_profiler.h" +#include +#include +#include #include #include #include #include -#include +#include +#include +#include +#include #include #include #include +#define PREAMBLE "Injector controller: " + using namespace std; using namespace wpl; @@ -38,25 +45,92 @@ namespace micro_profiler namespace { const auto secondary = agge::style::height(10); + const string c_profiler_module_32 = "micro-profiler_Win32.dll"; + const string c_profiler_module_64 = "micro-profiler_x64.dll"; + const string c_sandbox_32 = "micro-profiler_sandbox_Win32.exe"; + const string c_sandbox_64 = "micro-profiler_sandbox_x64.exe"; + const string c_injector_32 = "micro-profiler_injector_Win32.dll"; + const string c_injector_64 = "micro-profiler_injector_x64.dll"; struct pid { id_t operator ()(const process_info &record) const { return record.pid; } }; + + class injection_controller + { + public: + injection_controller(const string &dir, const string &frontend_endpoint_id) + : _dir(dir), _frontend_endpoint_id(frontend_endpoint_id), + _injector_32(create_injector(c_sandbox_32, c_injector_32)), + _injector_64(create_injector(c_sandbox_64, c_injector_64)) + { } + + void attach(process_info process) + { + if (const auto injector = process.architecture == process_info::x86 ? _injector_32 : _injector_64) + { + const auto profiler_module = process.architecture == process_info::x86 + ? c_profiler_module_32 : c_profiler_module_64; + + injection_info info = { + _dir & profiler_module, + _frontend_endpoint_id, + process.pid, + }; + auto &req = *_requests.insert(_requests.end(), shared_ptr()); + + LOG(PREAMBLE "requesting injection...") % A(this) % A(process.pid); + injector->request(req, request_injection, info, response_injected, [this, process] (ipc::deserializer &) { + LOG(PREAMBLE "injection complete") % A(this) % A(process.pid); + }); + } + } + + private: + shared_ptr create_injector(string sandbox, string injector) + { + sandbox = _dir & sandbox; + injector = _dir & injector; + + try + { + auto injector_ = make_shared([this, sandbox, injector] (ipc::channel &inbound) { + return ipc::spawn::connect_client(sandbox, vector(1, injector), inbound); + }); + + LOG(PREAMBLE "injector created...") % A(this) % A(sandbox) % A(injector); + return injector_; + } + catch (...) + { + LOGE(PREAMBLE "failed to create injector...") % A(this) % A(sandbox) % A(injector); + return nullptr; + } + } + + private: + const string _dir; + const string _frontend_endpoint_id; + shared_ptr _injector_32, _injector_64; + std::vector< std::shared_ptr > _requests; + }; } attach_ui::attach_ui(const factory &factory_, shared_ptr processes, const string &frontend_id) : wpl::stack(false, factory_.context.cursor_manager_) { + const auto controller = make_shared(~get_module_info(&secondary).path, frontend_id); shared_ptr toolbar; shared_ptr