/* * Copyright, Aspect Security, Inc. * * This file is part of JavaSnoop. * * JavaSnoop is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * JavaSnoop is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with JavaSnoop. If not, see <http://www.gnu.org/licenses/>. */ package com.aspect.snoop.util; import com.aspect.snoop.JavaSnoop; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; /** * * @author adabirsiaghi */ public class JadUtil { private static Map<String,String> codeCache; private static File getExecutableFromPath(String executableName) { String systemPath = System.getenv("PATH"); String[] pathDirs = systemPath.split(File.pathSeparator); File fullyQualifiedExecutable = null; for (String pathDir : pathDirs) { File file = new File(pathDir, executableName); if (file.isFile()) { fullyQualifiedExecutable = file; break; } } return fullyQualifiedExecutable; } public static String getJadLocation() { try { boolean isWin = System.getProperty("os.name").contains("Windows"); File f = getExecutableFromPath("jad" + (isWin ? ".exe":"") ); if ( f != null ) { return f.getAbsolutePath(); } /* * Check the Jad path. */ String jadPath = JavaSnoop.getProperty(JavaSnoop.JAD_PATH); if ( jadPath == null || jadPath.length() == 0 ) { return null; } if ( new File(jadPath).exists() ) { return jadPath; } } catch (Exception e) { } return null; } /** * This is the main public method for consumption. Takes a class name * and bytes, and returns a String containing the decompiled Java code. */ public static String getDecompiledJava(String className, byte[] clazz) throws IOException { String jadPath = getJadLocation(); if ( jadPath == null ) { throw new IOException("Couldn't find jad"); } if ( codeCache == null ) { codeCache = new HashMap<String,String>(); } else { String code = codeCache.get(className); if ( code != null ) { return code; } } String prefix = RandomUtil.randomString(8); File classFile = File.createTempFile(prefix, ".class"); FileOutputStream fos = new FileOutputStream(classFile); fos.write(clazz); fos.close(); String tmpDir = System.getProperty("java.io.tmpdir"); String cmd[] = { jadPath, "-o", "-d", tmpDir, "-s", ".java", classFile.getAbsolutePath() }; Process p = Runtime.getRuntime().exec(cmd); /* * Have to use this clever but kludgy hack to get around bug * in NT processes hanging indefinitely on p.waitFor() calls. * * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4254231 */ int rc = doWaitFor(p); /* * Now the decompiled file is waiting for us to grab and read in. */ String simpleClassName = getSimpleClassName(className); String javaFile = tmpDir + File.separatorChar + simpleClassName + ".java"; BufferedReader reader = new BufferedReader(new FileReader(javaFile)); StringBuffer sb = new StringBuffer(5000); String buff = null; String nl = System.getProperty("line.separator"); while ( (buff = reader.readLine()) != null ) { if ( !buff.startsWith("//") ) { sb.append(buff + nl); } } String finalCode = "/* Decompiled " + className + " */" + nl + sb.toString().trim(); codeCache.put(className, finalCode); /* * Delete the temporary file. */ File decompiledFile = new File(javaFile); decompiledFile.delete(); classFile.delete(); return finalCode; } private static String getSimpleClassName(String className) { String fqClassName = new String(className); int firstChar; firstChar = fqClassName.lastIndexOf('.') + 1; if (firstChar > 0) { className = fqClassName.substring(firstChar); } return className; } /** * Method to perform a "wait" for a process and return its exit value. * This is a workaround for <CODE>process.waitFor()</CODE> never returning. */ public static int doWaitFor(Process p) { int exitValue = -1; // returned to caller when p is finished try { InputStream in = p.getInputStream(); InputStream err = p.getErrorStream(); boolean finished = false; // Set to true when p is finished while( !finished) { try { while( in.available() > 0) { // Print the output of our system call Character c = new Character( (char) in.read()); System.out.print( c); } while( err.available() > 0) { // Print the output of our system call Character c = new Character( (char) err.read()); System.out.print( c); } // Ask the process for its exitValue. If the process // is not finished, an IllegalThreadStateException // is thrown. If it is finished, we fall through and // the variable finished is set to true. exitValue = p.exitValue(); finished = true; } catch (IllegalThreadStateException e) { // Process is not finished yet; // Sleep a little to save on CPU cycles Thread.currentThread().sleep(100); } } } catch (Exception e) { // unexpected exception! print it out for debugging... System.err.println( "doWaitFor(): unexpected exception - " + e.getMessage()); } // return completion status to caller return exitValue; } }