// 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 <windows.h>
#include <tchar.h>
#include <shellapi.h>
#include "sandbox/win/sandbox_poc/sandbox.h"
#include "base/logging.h"
#include "sandbox/win/sandbox_poc/main_ui_window.h"
#include "sandbox/win/src/sandbox.h"
#include "sandbox/win/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,
base::string16 * 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;
}
base::string16 entry_point_wide = arg_list[1];
base::string16 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;
base::string16 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;
}