逆コンパイルについて

つい先日、Javaのプログラムを作成しました。
作成というか既存のプログラムの修正です。
その際、あるクラスで定数だけの変更するものがありました。


修正完了後、eclipseでビルドを行い、
クラスファイルをテストマシンに配置して動作確認を行いました。
しかし、動作がおかしい。
後で分かったことですが、その定数だけを変更したクラスが古いままでした。
おそらくソースファイルを保存していなかったのかもしれません。


ソースを見るだけでは更新されたと本人は思っていたため、
別のロジック部分を変更したクラスに原因があると思って調査してましたが、
そういったことがあったため、定数だけを変更したクラスに原因があるということを
判明するのに時間がかかってしまいました。
もしかして・・・と思ってクラスファイルを全削除して、再ビルドして作成されたクラスを
配置したところ問題なく動作しました。
クラスファイルからソースの内容を確認できればそういったミスがあったことを確認できるのになぁ。
と思い、調べてみたところ、javaでは逆コンパイルが容易に行うことができるそうです。


実際に逆コンパイルを行うツールとしては、
Jad
というものがあります。


まず、「http://www.kpdus.com/jad.html
から「Download Jad」のページより、ファイルをダウンロードします。
使用方法は簡単で、ダウンロードしてきたファイルを解凍し、
コマンドプロンプトから
jad.exe 対象のクラスファイル
として実行するだけです。

実際に私が試した結果では、
実際のソース

package domain;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.List;

import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;

import entity.BookList;

public class BookListEntityManager {

	
	private String xmlPath;
	private String mappingPath;
	
	public String getMappingPath() {
		return mappingPath;
	}

	public void setMappingPath(String mappingPath) {
		this.mappingPath = mappingPath;
	}

	public BookList getBookList () {

		BookList bookList = null;
		
		try {

			// マッピングファイルを読み込み
			Mapping map = new Mapping();
			map.loadMapping(this.mappingPath);

			
			// Fileオブジェクトを生成し読み込みファイルを作成
			File file = new File(xmlPath);
			Reader read = new FileReader(file);
			
			// アンマーシャル(読込)を行うためのマーシャルオブジェクトを生成
			Unmarshaller unmarshaller = new Unmarshaller(map);
			
			// アンマーシャル
			bookList = (BookList) unmarshaller.unmarshal(read);
			
			return bookList;
			
		} catch (FileNotFoundException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (IOException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (MappingException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (MarshalException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (ValidationException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		}
		return null;
	}
	
	public void updateBookList (BookList bookList) {
		
		
		try {
//			 ファイルオブジェクトから書き込みファイルを生成
			File file = new File(xmlPath);
			Writer writer = new FileWriter(file);
			
			// マッピングファイルを読み込み
			Mapping map = new Mapping();
			map.loadMapping(this.mappingPath);
			
			// マーシャルオブジェクトの作成
			Marshaller marshaller = new Marshaller(writer);
			marshaller.setMapping(map);
			
			// マーシャル(書き込み)
			marshaller.marshal(bookList);			
			
		} catch (IOException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (MappingException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (MarshalException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (ValidationException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		}
		
	}
	public String getXmlPath() {
		return xmlPath;
	}
	public void setXmlPath(String xmlPath) {
		this.xmlPath = xmlPath;
	}	
}

をビルドし、生成されたクラスをjadを使用した結果、

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   BookListEntityManager.java

package domain;

import entity.BookList;
import java.io.*;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.xml.*;

public class BookListEntityManager
{

    public BookListEntityManager()
    {
    }

    public String getMappingPath()
    {
        return mappingPath;
    }

    public void setMappingPath(String mappingPath)
    {
        this.mappingPath = mappingPath;
    }

    public BookList getBookList()
    {
        BookList bookList = null;
        try
        {
            Mapping map = new Mapping();
            map.loadMapping(mappingPath);
            File file = new File(xmlPath);
            java.io.Reader read = new FileReader(file);
            Unmarshaller unmarshaller = new Unmarshaller(map);
            bookList = (BookList)unmarshaller.unmarshal(read);
            return bookList;
        }
        catch(FileNotFoundException e)
        {
            e.printStackTrace();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        catch(MappingException e)
        {
            e.printStackTrace();
        }
        catch(MarshalException e)
        {
            e.printStackTrace();
        }
        catch(ValidationException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    public void updateBookList(BookList bookList)
    {
        try
        {
            File file = new File(xmlPath);
            java.io.Writer writer = new FileWriter(file);
            Mapping map = new Mapping();
            map.loadMapping(mappingPath);
            Marshaller marshaller = new Marshaller(writer);
            marshaller.setMapping(map);
            marshaller.marshal(bookList);
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        catch(MappingException e)
        {
            e.printStackTrace();
        }
        catch(MarshalException e)
        {
            e.printStackTrace();
        }
        catch(ValidationException e)
        {
            e.printStackTrace();
        }
    }

    public String getXmlPath()
    {
        return xmlPath;
    }

    public void setXmlPath(String xmlPath)
    {
        this.xmlPath = xmlPath;
    }

    private String xmlPath;
    private String mappingPath;
}

というファイルが生成されました。
比べてみたところ、ほぼ一緒のものが生成されました!。
コメントが無かったり、変数定義場所が違ったりしていますが、
少なくともロジック上は同じものが生成されていますね。


これでクラスファイルの内容を確認したい時、
昔に作成したクラスファイルでソースが無くなってしまった時など、
簡単にソースが復元できそうです。
あまり使用する機会は無いと思いますが、
困った時には、有用なツールです。


なお、.netでも逆コンパイルはできるらしいです。
NET逆コンパイラとコードを難読化するDotfuscator
こちらも参考になりそうです。