ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [java] java runtime system 권한으로 실행
    case Computer : 2014. 7. 9. 14:45

    Java에서 외부 프로그램을 실행하기 위해 사용하는 방법은 여러가지가 있다.


    Process와 ProcessBuilder를 이용하는 방법이 있고

    일반적으로 사용하는 Runtime에 exec 를 이용하는 방법이 있다.


    Runtime은 내부적으로 ProcessBuilder를 사용한다.


    하지만 Runtime 혹은 Processbuilder를 이용하여 실행하면 Java 프로그램을 실행한 주체에 따라 권한을 가져간다.

    그렇기 때문에 가끔 권한 문제에 걸릴수 있다.


    이 문서를 따라온 사람이라면 runas를 사용하는 방법을 시도해봤을 것이다.

    runas를 해보지 않았다면 시도 하지마라. 내가 해본걸 다 공유해주겠다.


    이유는 runas /user:USER_NAME command  사용하게 되는데 실행시 USER_NAME 계정에 암호를 요구한다.


    암호부분이 문제다. 


    1. 계정에 암호를 없애면...

       > 계정에 암호를 없애면 runas로 실행을 못한다.


    2. pipe를 이용해서 암호 입력

      > echo PASSWORD | runas /user:USER_NAME command

      > 이방법은 오래 전에 보안문제로 막혔다.

      > runas 설명을 보면 반드시 프롬프트에서 암호를 입력할수 있게되어있다.


    3. runtime에서 outputstream을 이용하는 방법

      > runtime으로 실행한 프로그램에 대해 inputstream과 errorstream을 이용해 프로그램에 결과를 출력할수 있다.

      > 반대로 ouputstream을 이용해서 입력도 가능하겠다 생각되지만 안된다.(되는 방법을 아시는분은 부처핸섭)

      > 실행 직후에 ouputstream 대한 파일 stream이 바로 끊겨버린다.

     


    위해 방법으로는 windows 7이상에서는 동작을 안된다. 

    xp에서는 되는지 확인은 못해보았다.(아마 xp에서도 동작은 안할것으로 생각되어진다.)



    그럼 이제 가능한 방법으로 실행 해보자

    권한은 System권한으로 동작하는걸 확인했다.


    방법은 JNA(Java Native Access) library를 사용하여 동작시키는 방식이다.

    요즘 JNI(java Native Interface)와 비슷하면서 다른 녀석입니다. 차이점이 궁긍하신분은 구글신에게 물어봅시다



    필요한 library

     - jna.jar

     - jna_platform.jar

       > https://github.com/twall/jna 에서 다운로드 가능



    1. 위에 두 라이브러리를 프로젝트에 추가한다.


    2. Shell32X.java 클래스 파일 생성한다.


    Shell32x.java


    package com.seworks.medusa.controller; import java.util.Arrays; import java.util.List; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.WString; import com.sun.jna.platform.win32.Shell32; import com.sun.jna.platform.win32.WinDef.HINSTANCE; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinReg.HKEY; import com.sun.jna.win32.W32APIOptions; public interface Shell32X extends Shell32{ Shell32X INSTANCE = (Shell32X)Native.loadLibrary("shell32", Shell32X.class, W32APIOptions.UNICODE_OPTIONS); int SW_HIDE = 0; int SW_MAXIMIZE = 3; int SW_MINIMIZE = 6; int SW_RESTORE = 9; int SW_SHOW = 5; int SW_SHOWDEFAULT = 10; int SW_SHOWMAXIMIZED = 3; int SW_SHOWMINIMIZED = 2; int SW_SHOWMINNOACTIVE = 7; int SW_SHOWNA = 8; int SW_SHOWNOACTIVATE = 4; int SW_SHOWNORMAL = 1; /** File not found. */ int SE_ERR_FNF = 2; /** Path not found. */ int SE_ERR_PNF = 3; /** Access denied. */ int SE_ERR_ACCESSDENIED = 5; /** Out of memory. */ int SE_ERR_OOM = 8; /** DLL not found. */ int SE_ERR_DLLNOTFOUND = 32; /** Cannot share an open file. */ int SE_ERR_SHARE = 26; int SEE_MASK_NOCLOSEPROCESS = 0x00000040; int ShellExecute(int i, String lpVerb, String lpFile, String lpParameters, String lpDirectory, int nShow); boolean ShellExecuteEx(SHELLEXECUTEINFO lpExecInfo); public static class SHELLEXECUTEINFO extends Structure { /* DWORD cbSize; ULONG fMask; HWND hwnd; LPCTSTR lpVerb; LPCTSTR lpFile; LPCTSTR lpParameters; LPCTSTR lpDirectory; int nShow; HINSTANCE hInstApp; LPVOID lpIDList; LPCTSTR lpClass; HKEY hkeyClass; DWORD dwHotKey; union { HANDLE hIcon; HANDLE hMonitor; } DUMMYUNIONNAME; HANDLE hProcess; */ public int cbSize = size(); public int fMask; public HWND hwnd; public WString lpVerb; public WString lpFile; public WString lpParameters; public WString lpDirectory; public int nShow; public HINSTANCE hInstApp; public Pointer lpIDList; public WString lpClass; public HKEY hKeyClass; public int dwHotKey; /* * Actually: * union { * HANDLE hIcon; * HANDLE hMonitor; * } DUMMYUNIONNAME; */ public HANDLE hMonitor; public HANDLE hProcess; protected List getFieldOrder() { return Arrays.asList(new String[] { "cbSize", "fMask", "hwnd", "lpVerb", "lpFile", "lpParameters", "lpDirectory", "nShow", "hInstApp", "lpIDList", "lpClass", "hKeyClass", "dwHotKey", "hMonitor", "hProcess", }); } } }


    3. 그리고 아래 메소드를 정의해 준다. 

    public void executeAsAdministrator(String command, String args, String workingDir)
        {
    	
            SHELLEXECUTEINFO execInfo = new SHELLEXECUTEINFO();
            
            execInfo.lpFile = new WString(command);
            if (args != null)
                execInfo.lpParameters = new WString(args);
            if(workingDir != null)
            	execInfo.lpDirectory = new WString(workingDir);
            
            execInfo.nShow = Shell32X.SW_SHOWDEFAULT;
            execInfo.fMask = Shell32X.SEE_MASK_NOCLOSEPROCESS;
            execInfo.lpVerb = new WString("runas");
            
            
            boolean result = false;
            try{
            	result = Shell32X.INSTANCE.ShellExecuteEx(execInfo);
            }catch(Exception e){
            	e.printStackTrace();
            }
            
            if (!result){
            	
                int lastError = Kernel32.INSTANCE.GetLastError();
                String errorMessage = Kernel32Util.formatMessageFromLastErrorCode(lastError);
                
                System.out.println("Run fail");
                System.out.println("errorMessage : "+lastError);
                System.out.println("errorMessage : "+errorMessage);
                System.out.println("apperror : "+execInfo.hInstApp);
                
                throw new RuntimeException("Error performing elevation: " + lastError + ": " + errorMessage + " (apperror=" + execInfo.hInstApp + ")");
            }else{
            	System.out.println("Run Success");
            }
        }
    


    4. 사용법은 아래와 같다.


    String command = "netstat"; String arguments = "-an"; String working_directory = null; executeAsAdministrator(command , arguments, working_directory);



    위에 방법에도 단점은 있다. pipe를 통한 명령을 처리하는 작업은 또 불가능하다.


    위에 방법도 외부 프로그램을 실행하는 하나의 방법이기 때문에 상황에 맞는 방법을 선택하길 바란다.



    반응형

    댓글

Designed by Tistory.