삽질하는플머

원격 커멘드 - rcmd

탐구생활/Others
원격 컴퓨터의 콘솔 프로그램을 실행하고 그 결과를 내 컴퓨터에 뿌려줘야 한다면? 바로 떠오르는 해법은 SSHD 를 설치해 사용하는 것이다. 하지만 로컬 네트웍에서 간단한 작업을 위해 cygwin 깔고 SSHD 설정하고 키를 교환한다는 건... 왠지 소잡는 칼을 생선 다듬는 데 휘둘러대는 폼안나는 일이라 생각된다. 

해서 간단한 유틸리티를 만들어볼까 폼잡다가 윈도 2000 리소스킷에서 멋진 솔루션을 발견했다. 이름하여 rcmd / rcmdsvc !! 
오늘은 이 물건을 가지고 재미나게 놀아보자!! (검색 안해보고 만들었으면 속 좀 쓰릴 뻔...)

다운로드는 여기서 "Remote command service" 를 클릭하면 rcmd.zip 을 얻을 수 있다. 압축을 풀면 rcmd.exe 와 rcmdsvc.exe 가 나온다. 

(나중에 찾아가기 귀찮으니 걍 여기에 붙여두자)

1. rcmdsvc.exe 를 서비스로 등록. 

"Service Controller Query Tool" 이라 불리는 sc.exe 는 예전에는 리소스킷으로 제공되었었는데 XP에는 이미 포함되어있다. 
압축이 풀린 rcmd.exe 와 rcmdsvc.exe 를 윈도우의 System32 폴더로 옮기고 커맨드창에서 다음과 같이 입력하자. 

C:\>sc create rcmdsvc binPath= C:\WINDOWS\system32\Rcmdsvc.exe displayname= "Remote command service" start= auto

[SC] CreateService SUCCESS


제어판의 서비스 항목에 방금 설치한 Remote command service 가 제대로 들어갔는지 살펴보자. 




여기서 "시작" 버튼을 살포시 눌러주거나, net start rcmdsvc 또는 sc start rcmdsvc 로 서비스를 시작한다. 
(등록된 서비스를 삭제하는 명령은 sc delete rcmdsvc 이다.)


2. 로컬에서 테스트.

이제 준비된 서버에 접속해보자. rcmd.exe 를 과감하게 실행시킨다. 




서버의 이름을 입력하라는 안내가  나온다. 로컬 서버의 IP인 127.0.0.1 을 입력하면 서비스가 실행된 계정인 LocalService 의 홈디렉토리를 기준으로 셸이 열리게 된다. 

실행 후 서버의 CMD를 띄우는 방식 외에 다음과 같이 직접 명령어를 입력하고 결과를 받아올 수도 있다. 
(도메인 이름 앞에 역슬래시 두개가 붙는 것에 유의하자.)

C:\> rcmd \\127.0.0.1 dir c:\ /w


실행 결과는 다음과 같다. 




3. 원격지에서 접속.

로컬에서만 띄우면 이게 뭐지 싶을 것이다. 이제 원래 목적이었던 원격지에서의 접속을 테스트 해 보자. 
이 글에서 rcmdsvc 서비스를 동작시키는 컴퓨터의 내부 네트웍 IP 는 192.168.29.1 이다. 다른 컴퓨터에서 이 컴퓨터의 dir 명령을 내려보자. 

C:\> rcmd \\192.168.29.1 dir c:\ /w

Error - Failed to connect to <\\192.168.29.1>, Error = 1326


1326 에러는 대상 컴퓨터의 IPC 권한을 갖지 못했기 때문에 발생한다. 네트워크 공유 연결과 동일한 방식으로 권한을 획득해주자. 

C:\> set user=[사용자명]
C:\> set passwd=[비밀번호]]

C:\> net use \\192.168.29.1\IPC$ %PASSWD% /user:%USER%
명령을 잘 실행했습니다.


이제 앞에서 내렸던 rcmd 명령을 다시 실행하면 원격지의 C 드라이브 내용을 볼 수 있게 된다. 

확보한 IPC 권한은 다음과 같은 방법으로 반환할 수 있다. 

C:\>net use \\192.168.29.1\IPC$ /delete
\\192.168.29.1\IPC$이(가) 제거되었습니다.


콘솔창에서 입력이 아닌 델파이에서 대상지의 IPC 권한을 확보하는 방법은 예전에 델마당에 올렸던 강좌를 참고하자.


// 도메인에 연결을 만든다.
function UseConnection(const aDomain, aUserID, aPassWord: String; var ConnStr: String): Boolean;
var
  NetResource: TNetResource;
  ConnStrLen: DWORD;
  RetFlag: DWORD;
  RetValue: DWORD;
begin
  FillChar(NetResource, SizeOf(TNetResource), 0);

  with NetResource do
  begin
    dwType := RESOURCETYPE_ANY;
    lpLocalName := nil;   // 로컬 드라이브 지정하지 않음
    lpRemoteName := PChar('\\' + aDomain);
    lpProvider := nil;
  end;

  SetLength(ConnStr, MAX_PATH);
  ConnStrLen := MAX_PATH;
  RetValue:=
    WNetUseConnection(
      0, NetResource,
      PChar(aPassWord), PChar(aUserID), CONNECT_INTERACTIVE,
      PChar(ConnStr), ConnStrLen, RetFlag
    );
  SetLength(ConnStr, StrLen(PChar(ConnStr)));

  ConnStr := '\\'+aDomain;

  Result := RetValue = 0;
end;


var
  ConnStr: String;
...
  UseConnection('192.168.29.1\IPC$', 유저이름, 패스워드, ConnStr) 


이 호출이 True면 권한 확보가 된 상태이므로 rcmd 를 사용할 수 있게 된다. 다 사용한 권한은 WNetCancelConnection() API 로 반환한다. 

WNetCancelConnection(PChar(ConnStr), true)


응용범위는 사실 무궁무진한데... 예를 들어 여러대의 PC에서 메인 PC의 스케줄에 따라 rsync 로 동기화해야 할 경우도 이 방법을 사용하면 중앙의 한 대의 PC에서 간단히 해결할 수 있다. 또한 Screen 과 버무리면 지금 서비스중인 게임에서 SSH 를 걷어내고 설정을 간단히하는 용도로 사용할 수도 있을 것이다. 

쓰기 편한 방법은 항상 보안위험에 노출되어있다. 하지만 SSHD 를 띄우고 키를 교환해두는 것도 불편함을 해결하기 위해 보안은 나몰라라 하는 것은 마찬가지이고, 또한 IPC가 로컬네트웍에서만 유효하다는 것을 감안하면 생선 비늘 벗기는 용도로는 딱 알맞은 칼이라 생각된다. 




한가지 http://www.simpleisbest.net/archive/2005/10/18/260.aspx 이 사이트의 내용을 보면 "HKEY_CURRENT_USER 레지스트리에 접근하면 데스크탑에 로그온한 사용자의 레지스트리가 아닌 로컬 서비스 계정의 레지스트리가 액세스되어 버릴 것이다" 라는 무시무시한 구절이 있다. 흠.. 이건 좀 곤란한데... 진짜로 그런지 확인해보자. 

일단 로컬시스템에서 적당한 레지스트리를 익스포트 하자. (뭐하는데 쓰는지는 잘 모르겠지만 값이 달랑 하나라 선택)

regedit /E "C:\test.reg" HKEY_CURRENT_USER\Calendar


출력된 C:\test.reg 파일의 내용은 다음과 같다. 

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Calendar]
"Skin"="1"


이제 이 Skin 값을 뭐 한 100 으로 바꿔준 뒤, 원격지에서 임포트 할 때 제대로 들어가는지 확인해 보자.

rcmd \\192.168.29.1 regedit /S C:\test.reg


로컬로 돌아와 레지스트리 편집기를 열어서 확인해본다. 




흠... 제대로 잘 들어가는뎅... 





당연한 얘기지만 서비스를 Interactive Service 로 설정하지 않으면, 즉 데스크탑과 상호연동을 시키지 않으면 윈도를 띄우거나 하는 일은 불가능. 즉 rcmd 로 원격지의 "윈도 어플" 을 실행시키면 이 윈도는 LocalService 계정에서 실행되고 나타나지 않는다.  

심심해서 rcmdsvc 의 로그온 설정의 "서비스와 데스크톱 상호 작용 허용" 을 체크하고 재구동한 뒤 원격지에서 로컬컴퓨터의 맵서버를 구동시켜 봄. 원격지에서 조작윈도나 루아윈도를 띄우는 명령을 내리면 이 창은 모두 로컬에서 나타난다.




오래전 GPG 글에서 rcmd 관련된 언급을 발견하고 반가워서 트랙백을 걸어둠.