Local SYSTEM 권한으로 코드를 실행하는 방법
이에 대해서 검색해 보면, 여러 가지 글들이 나옵니다.
Starting Process in the Logged Session under the Local System Account
; http://www.codeproject.com/Articles/133375/Starting-Process-in-the-Logged-Session-under-the-L
종합해 보면, NT 서비스를 SYSTEM 권한으로 등록시키고 그것을 이용해 코드를 실행하라는 것입니다. 정말 이 방법 밖에는 없는 걸까요? 아쉽게도 그런 것 같습니다. ^^ "Mark Russinovich" 같은 뛰어난 개발자조차도 이 방법을 사용한 것에서 미뤄 짐작할 수 있습니다.
그가 만든 Sysinternals 도구 중의 하나인 psexec.exe를 이용하면 특정 프로세스를 Local SYSTEM 권한으로 실행할 수 있습니다.
SYSTEM 권한으로 UI 프로그램 실행하는 방법
; https://www.sysnet.pe.kr/2/1/1153
psexec.exe가 동작하는 방식은 다음과 같습니다.
- c:\windows\psexesvc.exe 파일이 있는지 확인한다. 파일이 없다면 psexec.exe 파일 내부의 리소스로 포함된 "PSEXESVC" 바이너리를 c:\windows\psexesvc.exe로 쓴다.
- SCM(Service Control Manager) Win32 API를 이용해 psexesvc.exe를 "Local SYSTEM" 권한의 NT 서비스로 등록한다.
- 인자로 입력된 프로그램 경로를 psexesvc.exe에 넘기고 실행한다.
- 프로그램 실행이 완료되면, SCM Win32 API를 이용해 psexesvc.exe의 NT 서비스 등록을 해제한다.
실제로, 3번 단계에서 프로그램이 실행되고 있을 때 "Process Explorer"를 이용해 해당 프로그램의 부모를 확인하면 psexesvc.exe인 것을 알 수 있고, NT 서비스에도 다음과 같이 등록된 것을 볼 수 있습니다.
제 경우에는 예전에 관리자 권한의 작업을 COM+에 대행했던 방법을 소개해 드린 적이 있는데요.
관리자 권한이 필요한 작업을 COM+에 대행
; https://www.sysnet.pe.kr/2/0/1290
gacutil 등을 이용한 등록 과정을 고려해 볼 때, 단발성으로 실행할 관리 권한의 코드라면 NT 서비스 등록/해제가 더 간단할 것도 같습니다.
이걸 C#으로 한번 구현해 볼까요? ^^ 다행히 SCM을 다루는 Win32 API를 다음의 글에서 C#으로 만들어 두었기 때문에 작업을 좀 더 쉽게 할 수 있습니다.
How to install a windows service programmatically in C#?
; http://stackoverflow.com/questions/358700/how-to-install-a-windows-service-programmatically-in-c
Re: Detecting a service that is running
; http://www.tech-archive.net/Archive/VB/microsoft.public.vb.winapi/2006-08/msg00238.html
이번 글에서는 모듈 수를 줄이기 위해 서비스 등록하는 프로그램과 서비스로 동작하는 프로그램을 하나로 만들어 볼 텐데요. 그래서, 아래와 같이 간단하게 코드가 나옵니다.
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using Microsoft.Win32;
namespace psexec2
{
static class Program
{
static string _svcName = "psexec2";
static void Main(string[] args)
{
if (Environment.UserInteractive == true)
{
// 일반 프로그램으로 실행된 경우
DanceWithSCM();
}
else
{
// Local System 권한의 NT 서비스로 실행된 경우
if (Environment.UserName == "SYSTEM")
{
DoSystemRights();
}
}
}
// Local System 권한의 NT 서비스로 실행된 경우
// 스스로 NT 서비스로 등록된 상태에서 실행되었으므로,
// 원하는 SYSTEM 권한의 동작을 수행한 후 곧바로 실행을 벗어난다.
private static void DoSystemRights()
{
using (RegistryKey regKey =
Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services", true))
{
RegistryKey created = regKey.CreateSubKey(".DoTest");
created.SetValue("User", Environment.UserName);
}
}
// 일반 프로그램으로 실행된 경우
// 1) 스스로를 NT 서비스로 등록하고 시작시킨 후,
// 2) 서비스로 실행된 프로세스가 종료되는 때까지 대기한 다음,
// 3) NT 서비스 등록을 해제한다.
private static void DanceWithSCM()
{
Process process = Process.GetCurrentProcess();
string filePath = typeof(Program).Assembly.Location;
if (ServiceInstaller.ServiceIsInstalled(_svcName) == false)
{
ServiceInstaller.InstallAndStart(_svcName, _svcName, filePath);
}
string exeName = Path.GetFileNameWithoutExtension(typeof(Program).Assembly.Location);
Process[] serviceProcess = Process.GetProcessesByName(exeName);
int maxSecond = 10;
if (serviceProcess.Length == 2)
{
foreach (Process targetProcess in serviceProcess)
{
if (targetProcess.Id == process.Id)
{
continue;
}
System.Diagnostics.Trace.WriteLine("Exceuted");
targetProcess.WaitForExit(maxSecond * 1000);
}
}
ServiceInstaller.StopService(_svcName);
while (ServiceInstaller.GetServiceStatus(_svcName) == ServiceState.Stop)
{
if (maxSecond-- <= 0)
{
break;
}
Thread.Sleep(200);
}
ServiceInstaller.Uninstall(_svcName);
}
}
}
위의 코드를 '관리자 권한'으로 실행하면 다음과 같이 레지스트리에 새로운 키가 생성됩니다.
첨부된 파일은 위의 예제 코드를 담고 있습니다.
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]