Class.forName 사용하기


JAVA.LANG.CLASS클래스


클래스의 정보를 얻기위한 클래스 

즉, 클래스의 정보를 얻어오는 클래스입니다.


forName() : 물리적인 클래스 파일명을 인자로 넣어주면 이에 해당하는 클래스를 반환해줍니다.

클래스를 조사하기 위한 클래스입니다.

변수로 클래스를 만들때 Class.forName 은 유용하게 쓰인다.


예제소스 (물리적인 WhitePerson 클래스를 인스턴스한다.)


try{

Class c = Class.forName("poly.WhitePerson");

// 로딩단계(클래스조사),메모리에 올라오지는 않음(newInstance()해줘야함)

Person p=(WhitePerson)c.newInstance();

// newInstance() 반환형이 Object형이므로 다운캐스팅한다.

}catch(ClassNotFoundException e1){

//클래스를 찾지못했을 경우에 대한 예외사항 

System.out.println("클래스가 존재하지 않습니다.");

}catch(InstantiationException e2){

//인스턴스(new)실패시에 대한 예외사항

System.out.println("메모리에 올릴수 없습니다.");

}catch(IllegalAccessException e3){

//파일접근에 대한 예외사항 

System.out.println("클래스파일 접근 오류입니다.");

}


종합예제 소스

물리적(폴더)에 있는 클래스 (WhitePerson/BlackPerson/YellowPerson)

를 입력받아 해당클래스를 로딩&생성하여 속성(color 변수)에 대한 값을 출력!!!!!

 

[1] 사용자가 입력한 문자열에 해당하는 클래스를 얻어옵니다.

[2] 얻어온 클래스를 메모리에 올린후 

[3] 해당 메서드를 실행합니다.


1. package classtest;

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import poly.*;


public class ClassApp extends JFrame implements ActionListener{

private JPanel p;

private JLabel la;

private JTextField txt1, txt2;

private JButton bt;


public ClassApp(){

p=new JPanel();

la=new JLabel("생성할 클래스 입력");

txt1=new JTextField(15);

txt2=new JTextField(15);

bt=new JButton("생성");

p.add(txt1);

p.add(bt);

p.add(txt2);

add(p);

bt.addActionListener(this);

setSize(200,150);

setVisible(true);

}


public void actionPerformed(ActionEvent ae){

String className = txt1.getText();


try{

Class c=Class.forName(className);

// 로딩단계(클래스조사), 메모리에 올라오지는 않음

Person p = (Person)c.newInstance();

txt2.setText(p.getColor());

}catch(ClassNotFoundException e1) {

//클래스를 찾지못했을 경우에 대한 예외사항

txt2.setText("클래스가 없습니다.");

}catch(InstantiationException e2){

// 인스턴스(new)실패시에 대한 예외사항

txt2.setText("인스턴스를 생성할수 없습니다.");

}catch(IllegalAccessException e3){

//파일접근에 대한 예외사항

txt2.setText("엑세스 할수 없습니다.");

}

}


public static void main(String[] args){

new ClassApp();

}

}


Class.forName()ClassLoader.loadClass() 차이점


일단 Class.forName() 메서드는 인자가 한 개 짜리인 것과 세 개 짜리인 것이 있습니다.


static Class<?>     forName(String className)

          Returns the Class object associated with the class or interface with the given string name.



static Class<?>     forName(String name, boolean initialize, ClassLoader loader)

          Returns the Class object associated with the class or interface with the given string name, using the given class loader.


1. 클래스를 로딩할 때 사용되는 클래스로더 차이


인자가 한개 짜리인 forName(String) 메서드는 클래스를 로딩할 때 사용하는 클래스로더가 저 코드를 실행하는 클래스로더가 됩니다. 하지만 ClassLoader.loadClass()를 사용하면 당연히 자기 자신을 사용해서 클래스 로딩을 실행하게 되죠. 

(그렇다고 해서 반드시 해당 클래스로더가 읽어온다는 보장은 없죠. 그 부모가 읽어올 수도 있고 클래스 패스에 없을 수도 있고 암튼 여기서 로딩한다는 건 로딩을 시도한다고 보시기 바랍니다.)


하지만 Class.forName(String, boolean, ClassLoader)를 사용하면 클래스 로더를 지정해 줄 수 있습니다.



2. 초기화


Class.forName(String) 메서드를 사용하면 곧바로 클래스의 static 초기화 블럭과 static 멤버 변수의 값을 초기화 합니다. 하지만 ClassLoader.loadClass()를 사용하면 해당 클래스를 처음으로 사용하기 전까지 초기화가 지연됩니다.

이것 역시 Class.forName(String, boolean, ClassLoader)의 두번째 인자값을 이용하여 조절할 수 있습니다.


- 클래스 초기화 에러

만약 Class.forName(String)을 사용해서 로딩할 때 static 영역에서 에러가 난다면 해당 클래스는 다시 로딩할 수가 없습니다. 특정 클래스로더가 일단 로딩한 클래스는 다시 로딩할 수가 없죠. 

그래서 NoClassDefinitionFound 에러가 날 수도 있습니다.

이때는 해당 클래스로더 인스턴스를 버리고 새로 만들어야 하는데 그럴 때를 대비해 인자 세개짜리 forName을 쓰라는군요.

결국 forName()으로 클래스를 로딩할 떄는 별 개의 클래스로더를 쓰라는건데 그렇게 단순해 보이지가 않는데 클래스로더를 지정해 둔다고 해봤자. 보통 App CL로 읽어올테고 그럼 App CL 인스턴스를 버리라고?? 그건 좀..

forName으로 읽어올 클래스를 클래스패스를 가지고 있으면서 parent로 위임하지도 않는 CL을 이용해서 forName으로 읽은 경우라면 뭐 괜찮을지도 모르겠습니다. 어쨋거나 직접 통제가 가능한 클래스로더를 사용해야 겠네요.

'JAVA' 카테고리의 다른 글

[JAVA] ClassLoader  (0) 2017.10.30
[JAVA]classes 위치 가져오기  (0) 2017.10.30
[JAVA] class 클래스 로딩  (0) 2017.10.30
[JAVA] class 동적로딩  (0) 2017.10.30
[JAVA] class path에서 resource 찾기  (0) 2017.10.30
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,

[JAVA] class 클래스 로딩

JAVA 2017. 10. 30. 13:31

class 클래스 로딩


CLASSPATH에 없는 클래스 로딩


java.lang.reflect를 이용하면 우리가 원하는 클래스에 대한 invoke가 가능하다는 것은 알고 있을 것이다.

하지만 classpath에 등록안되어진 클래스들에 대해서는 어떻게 할 것인가?

일일이 사용자에게 클래스 패스를 설정하게 할수만은 없는 일 입니다.

보통의 엔진들을 보게 되면 install되어진 디렉토리의 위치만을 세팅하도록 하고 있다.

set JAVA_HOME 이라던지

set ANT_HOME 이라던지

쉘스크립트에 의하여 그러한 것들을 정의하여 java process를 띄우곤 하는데 그러면 내가 ant.jar등을 등록하지 않았음에도 불구하고 해당 애플리케이션들이 잘 작동하는 이유는 무엇일까요?

그건은 바로 ClassLoader에 숨겨져 있습니다.


아래에서 보여지는 샘플코드는 classpath 프로퍼티에 등록이 되어지지 않은 클래스들에 대한 조작을 할 것입니다. 

그렇게 함으로서 우리들이 만든 애플리케이션이 별다른 클래스로딩 정책 없이도 작동이 될수 있습니다.

그러려면 또한 잘 알아야 하는것이 reflection API가 있습니다.


이 부분에서는 그러한 것을 생략하고 URLClassLoader를 이용하여 디렉토리나 jar파일을 등록하여 가져오는 방법을 설명하도록 하겠습니다.


ClassLoader클래스는 이미 1.0API부터 존재했으며 URLClassLoader1.2에 새롭게 추가된 클래스이다.


우리가 사용하는 파일시스템이 URL이란 이름하에 조작이 될 수 있다는 것을 우선 명심해주시기 바랍니다.


이유는 file:// 이란 URI를 사용하기 때문이다.


아래에서는 특정한 티렉토리 안의 jar 파일에 대한 class loading샘플을 보여줍니다.


import java.io.*; 

import java.net.*; 


public class ClassLoading { 

public static void main(String [] args) throws Exception { 

   // Create a File object on the root of the directory containing the class file 

    File file = 

new File("D:/_Develop/jmxSamples/customMBean/log4j-1.2.8.jar"); 

     

    try { 

      // Convert File to a URL 

      URL url = file.toURL();          

// file:/D:/_Develop/jmxSamples/customMBean/log4j-1.2.8.jar 

            URL[] urls = new URL[]{ url }; 

            System.out.println(urls); 

       

            // Create a new class loader with the directory 

            ClassLoader cl = new URLClassLoader(urls); 

            System.out.println(cl); 

       

            // Load in the class; Logger.class should be located in 

     // the directory file:/D:/_Develop/jmxSamples/customMBean/log4j-1.2.8.jar 

      

Class cls = cl.loadClass("org.apache.log4j.Logger"); 

            System.out.println(cls); 

     

    } catch (MalformedURLException e) { 

        e.printStackTrace(); 

    } catch (ClassNotFoundException e2) { 

          e2.printStackTrace(); 

    }

   

  }

}


위에서 보는 것처럼 디렉토리를 설정하거나 특정 jar 파일을 사용할 수 있도록 작성합니다.

특정파일이 가르키지 않으면 해당 디렉토리의 class파일들을 package형태로 참조하도록 할 수 있는데 해당 디렉토리에 대한 클래스 로딩 샘플을 아래와 같습니다.


import java.io.*; 

import java.net.*; 


public class ClassLoading {

//Create a File object on the root of the directory containing the class file

File file = 

new File("D:/_CVSDevelop/jca_hello_adapter/build/classes");


try {

// Convert File to a URL

URL url = file.toURL();

// file:/D:/_CVSDevelop/jca_hello_adapter/build

URL[] urls = new URL[]{ url };

System.out.println(urls);


// Create a new class loader with the directory 

ClassLoader cl = new URLClassLoader(urls);

System.out.println(cl);


// Load in the class; Test.class should be located in 

// the directory //file:/D:/_CVSDevelop/jca_hello_adapter/build/classes/com/b//ea/jca/test/Test 

Class cls = cl.loadClass("com.bea.jca.test.Test"); 

System.out.println(cls); 

}catch (MalformedURLException e){

e.printStackTrace(); 

}catch (ClassNotFoundException e2){

e2.printStackTrace();

}

}


위와 같은 경우에는 classpath의 root로 잡은 디렉토리를 기준의 package형태로 설정되 파일을 로딩하여 사용할수 있도록 한다.

이 이후의 코딩에는 class가 newInstance를 취한 후 method를 

invoking해야 하는 과정을 거치게 되는데 한 가지 주의할 점은 해당 클래스를 반드시 reflection API를 이용하여 호출해야 한다는 점이다.

대략 아래의 코드정도를 이용하여 main 메소드등을 호출하는 클래스를 작성할 수 있을 것이다.


public void invokeClass(String name, String[] args)

throws ClassNotFoundException, NoSuchMethodException,

InvocationTargetException

{

Class c = loadClass(name);

Method m = c.getMethod("main", new Class[] {args.getClass()});

m.setAccessible(true);

int mods = m.getModifiers(); 

if(m.getReturnType() != void.class || !Modifier.isStatic(mods) || !Modifier.isPublic(mods)){

throw new NoSuchMethodException("main");

}

try {

m.invoke(null, new Object[] { args }); 

}catch (IllegalAccessException e) { 

// This should not happen, as we have disabled //access checks 

}

}


위와 같은 샘플을 이용하게 되면 서버측 프로그램에 대한 작성을 해볼 수 있는 좋은 기회가 아닐까 생각 됩니다.



'JAVA' 카테고리의 다른 글

[JAVA]classes 위치 가져오기  (0) 2017.10.30
[JAVA] Class.forName 사용하기  (0) 2017.10.30
[JAVA] class 동적로딩  (0) 2017.10.30
[JAVA] class path에서 resource 찾기  (0) 2017.10.30
[JAVA] cache function  (0) 2017.10.27
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,

[JAVA] class 동적로딩

JAVA 2017. 10. 30. 12:49

class 동적로딩


보안 전무업체에서 사용하는 소스 취약점 점검툴로 운영중인(또는 개발중인) 소스를 점검해 보면 동적으로 Class를 로딩하는 부분이 취약 하다는 

보고서를 받게 되는 경우가 종종있습니다. 

이때 보고서에 적힌 취약점 명은 다음과 같을 경우가 있습니다.


국정원 취약점 : 2. 파일 다운로드 취약점


안행부 보안취약점 : 15. 무결성 검사없는 코드 다운로드 취약점


그리고 취약하다고 보고된 코드는 보통 다음과 같을수 있습니다.


String className = "sample.security.checksum.DesObject";


<<중략>>


Class loadedClass = Class.forName(className);

WorkerObj workerObj = (WorkerObj)loadedClass.newInstance();


이에 대한 조치를 다음과 같이 수행하라고 적혀 있다.


Class loadedClass = Class.forName(className);

if(loadedClass.getChecksum() == loadedClass.isChecksum() ){

WorkerObj workerObj = (WorkerObj)loadedClass.newInstance();

}


상기 취약점은 클래스가 동적으로 로딩되는 과정에서 다른 클래스로 대체되어 악성코드가 실행될 수 있다는 가정이 전제되어 있습니다.

따라서 점검툴은 소스상에 Class.forName() 코드가 보이면 무조건 취약 코드로 잡아 낸다. 이 과정에서 오탐도 많이 발생하는데, 예를 들어 동적으로 로딩되는 클래스가 단순히 WEB-INF>classes 밑에 놓여있는 클래스들일 경우에는 로딩 과정에서 다른 객체로 변경될 가능성이 거의 제로에 가까우므로 오탐으로 간주해도 되나, 원격지 서버에 있는 라이브러리들을 동적으로 로딩하는 경우에는 충분히 악성클래스로 대체 될 수 있으르모 가이드를 참고해서 적절한 조치를 취해야 합니다.


이때의 조치방법으로는 Object Checksum 이나 File Checksum 의 소스 코드를 소개합니다.


다음은 Object Checksum의 소스 코드입니다.


public static BigInterger getObjChecksum(Object obj) {

BigInteger bigInt;

MessageDigest mDigest = null;


if(obj == null) {

bigInt = BigInteger.ZERO;

}else{


ByteArrayOutputStream baos = new ByteArrayOutputStream();


try{

ObjectOutputStream oos = new ObjectOutputStream(baos);

oos.writeObject(obj);  

//직렬화된 객체를 ByteArrayOutputStream(baos)에 write한다.

oos.close();

mDigest = MessageDigest.getInstance("SHA-256"); 

//SHA-256 알고리즘을 사용한다.

mDigest.update(baos.toByteArray());

}catch (IOException e){

System.out.println(e.toString());

BigInteger.ZERO;

}catch (NoSuchAlgorithmException e) {

System.out.println(e.toString());

bigInt = BigInteger.ZERO; 

}

}


bigInt = new BigInteger(1, mDigest.digest());

return bigInt;

}


이렇게 작성을 하게 되면 1233459834509830455656566456464564665456 와 같은 해쉬코드가 반환됩니다.


구글링을 해보면 유사한 코드가 소개되어 있는데 대부분 해쉬 알고리즘으로 MD5를 사용한 코드가 공개되어 있습니다. 그러나 MD5는 보안 취약점이 다수 발견되어 현재는 사용하지 않는 알고리즘이므로 보다 강화된 SHA-256 알고리즘을 사용한 코드로 변경하였습니다.


다음은 File Checksum 의 소스 코드입니다.


public static String getFileChecksm(String filename){

String checksum;

byte[] byteData = createChecksum(filename);


StringBuffer sb = new StringBuffer();

for(int i = 0 ; i < byteData.length ; i++) {

sb.append(Integer.toString((byteData[i]&0xff) + 0x100, 16).substring(1));

}


checksum = sb.toString();

return checksum;

}


 public static byte[] createChecksum(String filename){

   

     byte[] result = null;

     InputStream fis;

     

     try {

         fis = new FileInputStream(filename);

         byte[] buffer = new byte[1024];

         MessageDigest mDigest = MessageDigest.getInstance("SHA-256");

         int numRead;

         do {

              numRead = fis.read(buffer);

              if (numRead > 0) {

                  mDigest.update(buffer, 0, numRead);

              }

         } while (numRead != -1);

        fis.close();

      

        result = mDigest.digest();

    } catch (FileNotFoundException e) {

        System.out.println(e.toString());

    } catch (NoSuchAlgorithmException e) {

        System.out.println(e.toString());

    } catch (IOException e) {

        System.out.println(e.toString());

    }

  

    return result;

  }


//c0bbc7bcf23736a8560f68f7af73cd974f4a48ab7bb79cb05cedab5c4549990//와 같은 해쉬코드가 반환됩니다.

'JAVA' 카테고리의 다른 글

[JAVA] Class.forName 사용하기  (0) 2017.10.30
[JAVA] class 클래스 로딩  (0) 2017.10.30
[JAVA] class path에서 resource 찾기  (0) 2017.10.30
[JAVA] cache function  (0) 2017.10.27
[JAVA] BigInteger형을 바이트 배열로!!  (0) 2017.10.27
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,