UAC.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. #include "stdafx.h"
  2. #include <Windows.h>
  3. #include <memory>
  4. #include <string>
  5. #include <process.h>
  6. #include <codecvt>
  7. #include <tuple>
  8. #include <io.h>
  9. #include <fcntl.h>
  10. #define CONTROL_MAGIC 0xDEADBEEF
  11. #define CONTROL_WINDOW_SIZE 1
  12. #define CONTROL_KILL 2
  13. HPCON hPC = INVALID_HANDLE_VALUE;
  14. HANDLE pipeControl;
  15. PROCESS_INFORMATION childProcess;
  16. HRESULT CreatePseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut)
  17. {
  18. HRESULT hr = E_UNEXPECTED;
  19. HANDLE hPipePTYIn = INVALID_HANDLE_VALUE;
  20. HANDLE hPipePTYOut = INVALID_HANDLE_VALUE;
  21. // Create the pipes to which the ConPTY will connect
  22. if (CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) &&
  23. CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0))
  24. {
  25. // Determine required size of Pseudo Console
  26. COORD consoleSize = { 0 };
  27. CONSOLE_SCREEN_BUFFER_INFO csbi;
  28. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  29. if (GetConsoleScreenBufferInfo(hConsole, &csbi))
  30. {
  31. consoleSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
  32. consoleSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
  33. }
  34. hr = CreatePseudoConsole(consoleSize, hPipePTYIn, hPipePTYOut, 0, phPC);
  35. if (INVALID_HANDLE_VALUE != hPipePTYOut) CloseHandle(hPipePTYOut);
  36. if (INVALID_HANDLE_VALUE != hPipePTYIn) CloseHandle(hPipePTYIn);
  37. }
  38. return hr;
  39. }
  40. HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC)
  41. {
  42. HRESULT hr = E_UNEXPECTED;
  43. if (pStartupInfo)
  44. {
  45. SIZE_T attrListSize;
  46. pStartupInfo->StartupInfo.cb = sizeof(STARTUPINFOEX);
  47. // Get the size of the thread attribute list.
  48. InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);
  49. // Allocate a thread attribute list of the correct size
  50. pStartupInfo->lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attrListSize));
  51. // Initialize thread attribute list
  52. if (pStartupInfo->lpAttributeList
  53. && InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize))
  54. {
  55. // Set Pseudo Console attribute
  56. hr = UpdateProcThreadAttribute(
  57. pStartupInfo->lpAttributeList,
  58. 0,
  59. PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
  60. hPC,
  61. sizeof(HPCON),
  62. NULL,
  63. NULL)
  64. ? S_OK
  65. : HRESULT_FROM_WIN32(GetLastError());
  66. }
  67. else
  68. {
  69. hr = HRESULT_FROM_WIN32(GetLastError());
  70. }
  71. }
  72. return hr;
  73. }
  74. #define TC_NONE 0
  75. #define TC_UTF16_TO_UTF8 1
  76. #define TC_UTF8_TO_UTF16 2
  77. void __cdecl PipeWriter(LPVOID info)
  78. {
  79. auto pipes = (std::tuple<HANDLE, HANDLE, int>*)info;
  80. auto fIn = std::get<0>(*pipes);
  81. auto fOut = std::get<1>(*pipes);
  82. auto tcMode = std::get<2>(*pipes);
  83. tcMode = TC_NONE;
  84. const DWORD BUFSIZE = 512;
  85. char szBuffer[BUFSIZE];
  86. std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conversion;
  87. auto readSucceeded = FALSE;
  88. DWORD bytesRead, totalBytesRead;
  89. do
  90. {
  91. totalBytesRead = 0;
  92. do {
  93. readSucceeded = ReadFile(fIn, szBuffer + totalBytesRead, BUFSIZE - totalBytesRead, &bytesRead, NULL);
  94. totalBytesRead += bytesRead;
  95. if (!readSucceeded || bytesRead <= 0) {
  96. break;
  97. }
  98. if (szBuffer[totalBytesRead - 1] != '\x1b') {
  99. break;
  100. }
  101. } while (true);
  102. if (tcMode == TC_NONE) {
  103. WriteFile(fOut, szBuffer, totalBytesRead, NULL, NULL);
  104. }
  105. else if (tcMode == TC_UTF16_TO_UTF8) {
  106. std::string utf8String = conversion.to_bytes(std::wstring((const wchar_t*)szBuffer, totalBytesRead / 2));
  107. WriteFile(fOut, utf8String.c_str(), utf8String.length(), NULL, NULL);
  108. }
  109. else if (tcMode == TC_UTF8_TO_UTF16) {
  110. std::wstring utf16String = conversion.from_bytes(szBuffer, szBuffer + totalBytesRead);
  111. //for (int i = 0; i < utf16String.length(); i++) {
  112. //utf16String[i] = _byteswap_ushort(utf16String[i]);
  113. //}
  114. //utf16String = L"тест";
  115. WriteFile(fOut, utf16String.c_str(), utf16String.length() * 2, NULL, NULL);
  116. }
  117. } while (readSucceeded && bytesRead >= 0);
  118. }
  119. void __cdecl InputListener(LPVOID info) {
  120. std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conversion;
  121. auto pipes = (std::pair<HANDLE, HANDLE>*)info;
  122. HANDLE input = GetStdHandle(STD_INPUT_HANDLE);
  123. constexpr int bufferSize = 128;
  124. INPUT_RECORD records[bufferSize];
  125. DWORD numRead;
  126. while (true)
  127. {
  128. if (!ReadConsoleInput(input, records, bufferSize, &numRead)) {
  129. return;
  130. }
  131. for (int i = 0; i < numRead; i++) {
  132. if (records[i].EventType == KEY_EVENT && records[i].Event.KeyEvent.bKeyDown) {
  133. wchar_t ch[2] = { records[i].Event.KeyEvent.uChar.UnicodeChar, 0 };
  134. if (ch[0] < 128) {
  135. WriteFile(pipes->second, &records[i].Event.KeyEvent.uChar.AsciiChar, sizeof(char), NULL, NULL);
  136. }
  137. else {
  138. std::string utf8String = conversion.to_bytes(std::wstring(ch));
  139. WriteFile(pipes->second, utf8String.c_str(), utf8String.length(), NULL, NULL);
  140. }
  141. }
  142. if (records[i].EventType == WINDOW_BUFFER_SIZE_EVENT) {
  143. auto size = (WINDOW_BUFFER_SIZE_RECORD)records[i].Event.WindowBufferSizeEvent;
  144. auto width = size.dwSize.X;
  145. auto height = size.dwSize.Y;
  146. uint32_t buffer[] = { CONTROL_MAGIC, CONTROL_WINDOW_SIZE, width, height };
  147. WriteFile(pipeControl, buffer, sizeof(buffer), NULL, NULL);
  148. }
  149. }
  150. }
  151. }
  152. void __cdecl ControlListener(LPVOID info)
  153. {
  154. const DWORD BUFSIZE = 128;
  155. uint32_t buffer[BUFSIZE / sizeof(uint32_t)];
  156. SHORT width = 0, height = 0;
  157. DWORD bytesRead;
  158. auto readSucceeded = FALSE;
  159. do
  160. {
  161. readSucceeded = ReadFile(pipeControl, buffer, BUFSIZE, &bytesRead, NULL);
  162. if (buffer[0] == CONTROL_MAGIC) {
  163. if (buffer[1] == CONTROL_WINDOW_SIZE) {
  164. auto newWidth = (SHORT)buffer[2];
  165. auto newHeight = (SHORT)buffer[3];
  166. printf("Control: resize %ix%i\n", newWidth, newHeight);
  167. if (newWidth != width || newHeight != height) {
  168. width = newWidth;
  169. height = newHeight;
  170. ResizePseudoConsole(hPC, { width, height });
  171. }
  172. }
  173. if (buffer[1] == CONTROL_KILL) {
  174. printf("Control: kill\n");
  175. TerminateProcess(childProcess.hProcess, 1);
  176. ExitProcess(1);
  177. }
  178. }
  179. } while (readSucceeded && bytesRead >= 0);
  180. }
  181. BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
  182. {
  183. uint32_t buffer[] = { CONTROL_MAGIC, CONTROL_KILL };
  184. WriteFile(pipeControl, buffer, sizeof(buffer), NULL, NULL);
  185. return FALSE;
  186. }
  187. int wmain(int argc, wchar_t* argv[])
  188. {
  189. if (argc > 1 && lstrcmp(argv[1], L"--pipe") != 0) {
  190. wchar_t pipeName[256];
  191. wsprintf(pipeName, L"\\\\.\\pipe\\uac-pty-%i", rand());
  192. HANDLE pipeIn = CreateNamedPipe((std::wstring(pipeName) + L"-in").c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 256, 256, 0, NULL);
  193. HANDLE pipeOut = CreateNamedPipe((std::wstring(pipeName) + L"-out").c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 256, 256, 0, NULL);
  194. pipeControl = CreateNamedPipe((std::wstring(pipeName) + L"-control").c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 256, 256, 0, NULL);
  195. std::wstring cmd = L"--pipe " + std::wstring(pipeName) + L" ";
  196. for (int i = 1; i < argc; i++) {
  197. cmd += L"\"";
  198. cmd += argv[i];
  199. cmd += L"\" ";
  200. }
  201. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  202. DWORD consoleMode;
  203. GetConsoleMode(hConsole, &consoleMode);
  204. SetConsoleMode(hConsole, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
  205. hConsole = GetStdHandle(STD_INPUT_HANDLE);
  206. GetConsoleMode(hConsole, &consoleMode);
  207. SetConsoleMode(hConsole, ENABLE_WINDOW_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT);
  208. SHELLEXECUTEINFO ShExecInfo = { 0 };
  209. ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
  210. ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
  211. ShExecInfo.hwnd = NULL;
  212. ShExecInfo.lpVerb = L"runas";
  213. //ShExecInfo.lpVerb = NULL;
  214. ShExecInfo.lpFile = argv[0];
  215. ShExecInfo.lpParameters = cmd.c_str();
  216. ShExecInfo.lpDirectory = NULL;
  217. ShExecInfo.nShow = SW_HIDE;
  218. ShExecInfo.hInstApp = NULL;
  219. ShellExecuteEx(&ShExecInfo);
  220. SetConsoleCtrlHandler(CtrlHandler, TRUE);
  221. //_setmode(_fileno(stdout), _O_U16TEXT);
  222. SetConsoleOutputCP(CP_UTF8);
  223. Sleep(1000);
  224. _beginthread(PipeWriter, 0, new std::tuple<HANDLE, HANDLE, int>{ pipeOut, GetStdHandle(STD_OUTPUT_HANDLE), TC_UTF8_TO_UTF16 });
  225. _beginthread(InputListener, 0, new std::pair<HANDLE, HANDLE>{ GetStdHandle(STD_INPUT_HANDLE), pipeIn });
  226. WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
  227. CloseHandle(ShExecInfo.hProcess);
  228. return 0;
  229. }
  230. HANDLE pipeIn = CreateFile((std::wstring(argv[2]) + L"-in").c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
  231. HANDLE pipeOut = CreateFile((std::wstring(argv[2]) + L"-out").c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  232. pipeControl = CreateFile((std::wstring(argv[2]) + L"-control").c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
  233. std::wstring cmd = L"";
  234. for (int i = 3; i < argc; i++) {
  235. cmd += L"\"";
  236. cmd += argv[i];
  237. cmd += L"\" ";
  238. }
  239. // Create the Pseudo Console and pipes to it
  240. HANDLE hPipeIn{ INVALID_HANDLE_VALUE };
  241. HANDLE hPipeOut{ INVALID_HANDLE_VALUE };
  242. auto hr = CreatePseudoConsoleAndPipes(&hPC, &hPipeIn, &hPipeOut);
  243. if (S_OK == hr)
  244. {
  245. // Create & start thread to listen to the incoming pipe
  246. // Note: Using CRT-safe _beginthread() rather than CreateThread()
  247. _beginthread(PipeWriter, 0, new std::tuple<HANDLE, HANDLE, int>{ hPipeIn, pipeOut, TC_NONE });
  248. _beginthread(PipeWriter, 0, new std::tuple<HANDLE, HANDLE, int>{ pipeIn, hPipeOut, TC_NONE });
  249. _beginthread(ControlListener, 0, new std::pair<HANDLE, HANDLE>{ pipeControl, hPipeOut });
  250. // Initialize the necessary startup info struct
  251. STARTUPINFOEX startupInfo{};
  252. if (S_OK == InitializeStartupInfoAttachedToPseudoConsole(&startupInfo, hPC))
  253. {
  254. // Launch ping to emit some text back via the pipe
  255. hr = CreateProcessW(
  256. NULL, // No module name - use Command Line
  257. (LPWSTR)cmd.c_str(), // Command Line
  258. NULL,
  259. NULL,
  260. FALSE,
  261. EXTENDED_STARTUPINFO_PRESENT, // Creation flags
  262. NULL, // Use parent's environment block
  263. NULL, // Use parent's starting directory
  264. &startupInfo.StartupInfo, // Pointer to STARTUPINFO
  265. &childProcess) // Pointer to PROCESS_INFORMATION
  266. ? S_OK
  267. : GetLastError();
  268. if (S_OK == hr)
  269. {
  270. Sleep(500);
  271. // Wait up to 10s for ping process to complete
  272. WaitForSingleObject(childProcess.hProcess, INFINITE);
  273. // Allow listening thread to catch-up with final output!
  274. Sleep(500);
  275. }
  276. // --- CLOSEDOWN ---
  277. // Now safe to clean-up client app's process-info & thread
  278. CloseHandle(childProcess.hThread);
  279. CloseHandle(childProcess.hProcess);
  280. // Cleanup attribute list
  281. DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
  282. free(startupInfo.lpAttributeList);
  283. }
  284. // Close ConPTY - this will terminate client process if running
  285. ClosePseudoConsole(hPC);
  286. // Clean-up the pipes
  287. if (INVALID_HANDLE_VALUE != hPipeOut) CloseHandle(hPipeOut);
  288. if (INVALID_HANDLE_VALUE != hPipeIn) CloseHandle(hPipeIn);
  289. CloseHandle(pipeIn);
  290. CloseHandle(pipeOut);
  291. }
  292. return S_OK == hr ? EXIT_SUCCESS : EXIT_FAILURE;
  293. }