Skip to content

Instantly share code, notes, and snippets.

@RavuAlHemio
Created January 27, 2014 09:13
Show Gist options
  • Select an option

  • Save RavuAlHemio/8645421 to your computer and use it in GitHub Desktop.

Select an option

Save RavuAlHemio/8645421 to your computer and use it in GitHub Desktop.
runas-stdio: A variant of runas which takes all its parameters as UTF-8 strings via stdio.
// Released into the public domain.
// http://creativecommons.org/publicdomain/zero/1.0/
#include <string>
#include <cstdio>
#include <fcntl.h>
#include <io.h>
#include <windows.h>
/** The name of the program, taken from wargv[0]. */
static const wchar_t *progname = L"runas-stdio";
static std::wstring widen(std::string str)
{
if (str.length() == 0)
{
return std::wstring();
}
// what's the size?
int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), static_cast<int>(str.length()), nullptr, 0);
if (size == 0)
{
fwprintf(stderr, L"%ls: Failed to deduce buffer size for wide string: %lu\n", progname, GetLastError());
abort();
}
std::wstring ret(static_cast<unsigned int>(size), L'\0');
if (MultiByteToWideChar(CP_UTF8, 0, str.c_str(), static_cast<int>(str.length()), &ret[0], static_cast<int>(ret.size())) != size)
{
fwprintf(stderr, L"%ls: Failed to convert UTF-8 string to wide string: %lu\n", progname, GetLastError());
abort();
}
return ret;
}
static std::string readUntilNul(const wchar_t *readingWhat)
{
std::string ret;
int c;
for (;;)
{
c = fgetc(stdin);
if (c == '\0')
{
break;
}
else if (c == EOF)
{
fwprintf(stderr, L"%ls: Encountered error or EOF while reading \n", readingWhat);
abort();
}
else
{
ret += static_cast<char>(c);
}
}
return ret;
}
int wmain(int argc, wchar_t **wargv)
{
if (argc > 0 && wargv != nullptr && wargv[0] != nullptr && wargv[0][0] != L'\0')
{
progname = wargv[0];
}
if (argc > 1)
{
fwprintf(stderr,
L"\n"
L"Usage: %ls\n"
L"\n"
L"Send the following information in UTF-8 on stdin:\n"
L"1. The username (DOMAIN\\User or [email protected]).\n"
L"2. A NUL byte.\n"
L"3. The password.\n"
L"4. A NUL byte.\n"
L"5. The full command line to execute.\n"
L"6. A NUL byte.\n",
progname
);
return 1;
}
// set stdin to binary
_setmode(_fileno(stdin), _O_BINARY);
// username, then password, then command
std::string username = readUntilNul(L"username");
std::string password = readUntilNul(L"password");
std::string command = readUntilNul(L"command");
std::string::size_type backslash = username.find("\\");
bool haveDomain = false;
std::string domain;
if (backslash != std::string::npos)
{
// split username into domain and password
haveDomain = true;
domain = username.substr(0, backslash);
username = username.substr(backslash + 1);
}
// we'll need this as a modifiable buffer
std::wstring wideCommand = widen(command);
// go
STARTUPINFOW si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
int ret = CreateProcessWithLogonW(
widen(username).c_str(),
haveDomain ? widen(domain).c_str() : nullptr,
widen(password).c_str(),
LOGON_WITH_PROFILE,
nullptr, // application name (part of command line)
&wideCommand[0],
CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
nullptr, // environment (inherit)
nullptr, // working dir (inherit)
&si,
&pi
);
if (ret == 0)
{
fwprintf(stderr, L"%ls: Process launch failed: %lu\n", progname, ret);
abort();
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment