일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- MSSQL PYTHON
- MEAN Stack
- tensorflow
- Visual Studio Code
- 오라클
- MX Component
- 크롤링
- M2M
- scrapy
- 자본주의
- oracle
- sql developer
- MSSQL
- c#
- rs422
- matplot
- 텐서플로우
- pymssql
- Python
- Serial
- windows10
- rs485
- django
- 티스토리 초대장
- 윈도우10
- vscode
- 파이썬
- 딥러닝
- 장고
- PYTHON MSSQL
Archives
- Today
- Total
안까먹을라고 쓰는 블로그
[C#] 윈도우 서비스 프로젝트로 UI프로그램 실행하는 방법 & 화면 최상위로 올리기 본문
반응형
Window VISTA 부터 Windows Service 프로그램은 일반 콘솔이나 UI 프로그램과 달리, Session 0이라고 불리는 백그라운드에서 실행되어 사용자가 UI출력을 볼수 없다.
이유는 Session 0은 윈도우 핸들이 생기지 않기 때문에 UI가 없어서 이다. 0번이 아닌 다른 Session ID로 실행시켜야 한다.
원리는 아래와 같다.
1. Window API 를 써서 CONSOLE SESSION을 얻어옴. (대개 1임)
2. 윈도루 로그인 된 상태인지 확인한다. (Winlogon.exe 가 콘솔 Session ID로 실행 중인지 윈도우 API로 확인)
3. winlogon.exe의 PID를 가져옴, 위 그림에서는 1052 임
4. OpenProcess 윈도우 API를 이용하여, winlogon의 프로세스 핸들을 열어준다.
5. 프로세스 권한을 복제한 후, 실행시킬 프로그램을 USER가 실행시키는 것처럼 가져온 SESSION ID로 실행시켜준다.
소스코드
[메인]
protected override void OnStart(string[] args)
{
// 윈도우서비스 프로그램에서 UI프로그램 실행 시, 화면에 보이게 (백그라운드 XX)
ApplicationLoader.PROCESS_INFORMATION procinfo;
ApplicationLoader.StartProcessAndBypassUAC("실행파일경로\"실행파일.EXE", out procinfo);
// 화면 최상위로 올리기
ApplicationLoader.ActivateApp(SERVICENAME);
}
[WINDOWS API 참조 클래스] ApplicationLoader.cs
using System;
using System.Security;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
namespace API_STKIDS_ServerPrg_Service
{
/// <summary>
/// Class that allows running applications with full admin rights. In
/// addition the application launched will bypass the Vista UAC prompt.
/// </summary>
public class ApplicationLoader
{
#region Structures
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
#endregion
#region Enumerations
enum TOKEN_TYPE : int
{
TokenPrimary = 1,
TokenImpersonation = 2
}
enum SECURITY_IMPERSONATION_LEVEL : int
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
#endregion
#region Constants
public const int TOKEN_DUPLICATE = 0x0002;
public const uint MAXIMUM_ALLOWED = 0x2000000;
public const int CREATE_NEW_CONSOLE = 0x00000010;
public const int IDLE_PRIORITY_CLASS = 0x40;
public const int NORMAL_PRIORITY_CLASS = 0x20;
public const int HIGH_PRIORITY_CLASS = 0x80;
public const int REALTIME_PRIORITY_CLASS = 0x100;
#endregion
#region Win32 API Imports
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
static extern uint WTSGetActiveConsoleSessionId();
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
[DllImport("kernel32.dll")]
static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
[DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);
#endregion
/// <summary>
/// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt
/// </summary>
/// <param name="applicationName">The name of the application to launch</param>
/// <param name="procInfo">Process information regarding the launched application that gets returned to the caller</param>
/// <returns></returns>
public static bool StartProcessAndBypassUAC(String applicationName, out PROCESS_INFORMATION procInfo)
{
uint winlogonPid = 0;
IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
procInfo = new PROCESS_INFORMATION();
// obtain the currently active session id; every logged on user in the system has a unique session id
uint dwSessionId = WTSGetActiveConsoleSessionId();
// obtain the process id of the winlogon process that is running within the currently active session
Process[] processes = Process.GetProcessesByName("winlogon");
foreach (Process p in processes)
{
if ((uint)p.SessionId == dwSessionId)
{
winlogonPid = (uint)p.Id;
}
}
// obtain a handle to the winlogon process
hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
{
CloseHandle(hProcess);
return false;
}
// Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
// I would prefer to not have to use a security attribute variable and to just
// simply pass null and inherit (by default) the security attributes
// of the existing token. However, in C# structures are value types and therefore
// cannot be assigned the null value.
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
// copy the access token of the winlogon process; the newly created token will be a primary token
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
{
CloseHandle(hProcess);
CloseHandle(hPToken);
return false;
}
// By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
// the window station has a desktop that is invisible and the process is incapable of receiving
// user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user
// interaction with the new process.
STARTUPINFO si = new STARTUPINFO();
si.cb = (int)Marshal.SizeOf(si);
si.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop
// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
// create a new process in the current user's logon session
bool result = CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
applicationName, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
IntPtr.Zero, // pointer to new environment block
null, // name of current directory
ref si, // pointer to STARTUPINFO structure
out procInfo // receives information about new process
);
// invalidate the handles
CloseHandle(hProcess);
CloseHandle(hPToken);
CloseHandle(hUserTokenDup);
return result; // return the result
}
// 화면 최상위로 올리기
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string SClassName, string SWindowName);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr findname);
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr findname, int howShow);
private const int showNORMAL = 1;
private const int showMINIMIZED = 2;
private const int showMAXIMIZED = 3;
public static void ActivateApp(string processName)
{
// 프로그램명으로 핸들을 찾음
IntPtr findname = FindWindow(null, processName);
if (!findname.Equals(IntPtr.Zero))
{
// 프로그램이 최소화 되어 있다면 활성화 시킴
ShowWindowAsync(findname, showNORMAL);
// 윈도우에 포커스를 줘서 최상위로 만듬
SetForegroundWindow(findname);
}
}
}
}
[참조경로]
https://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-32-and-64-bit-Archite
https://dev-dudfufl.tistory.com/94
반응형
'Language > C#' 카테고리의 다른 글
C# DataTable에서 특정 컬럼만 Select해서 DataTable 만들기 (0) | 2022.07.06 |
---|---|
[C#] JSON 처리방법 (0) | 2022.07.03 |
[C#] 프로세스 상태 체크 (0) | 2022.06.29 |
[C#] DataRow Copy (0) | 2022.06.14 |
[C#] 컴파일러 오류번호로 문제사항 확인 및 조치 (0) | 2022.04.15 |
Comments