/*
Copyright 2012 Jan Ove Saltvedt
This file is part of KBot.
KBot 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.
KBot 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 KBot. If not, see <http://www.gnu.org/licenses/>.
*/
package com.kbotpro.utils;
import com.kbotpro.bot.injector.ClassData;
import com.kbotpro.ui.BotPanel;
import com.sun.org.apache.bcel.internal.Constants;
import com.sun.org.apache.bcel.internal.classfile.ClassParser;
import com.sun.org.apache.bcel.internal.classfile.ConstantString;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Method;
import com.sun.org.apache.bcel.internal.generic.*;
import com.sun.org.apache.bcel.internal.util.InstructionFinder;
import org.apache.log4j.Logger;
import java.io.DataInputStream;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.security.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Created by IntelliJ IDEA.
* User: Jan Ove Saltvedt
* Date: Oct 8, 2009
* Time: 11:22:48 AM
* To change this template use File | Settings | File Templates.
*/
public class RSLoaderClassLoader extends ClassLoader {
public final HashMap<String, byte[]> classes = new HashMap<String, byte[]>();
private ProtectionDomain domain;
private File jarFile;
CodeSource codeSource;
public RSLoaderClassLoader(File jarFile) {
super();
this.jarFile = jarFile;
try {
codeSource = new CodeSource(new URL("http://runescape.com/"), (CodeSigner[]) null);
domain = new ProtectionDomain(codeSource, createPermissions());
JarFile jar = new JarFile(jarFile);
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
if (name.endsWith(".class")) {
JavaClass javaClass = new ClassParser(new DataInputStream(jar.getInputStream(entry)), name).parse();
ClassGen classGen = new ClassGen(javaClass);
boolean somethingChanged = false;
if (classGen.getConstantPool().lookupString("random.dat") != -1) {
// UID HACK
int random = (int) (Math.random() * Integer.MAX_VALUE);
ConstantPoolGen cpg = classGen.getConstantPool();
((ConstantString) cpg.getConstant(cpg.lookupString("random.dat"))).setStringIndex(
cpg.addUtf8("random-" + random + ".dat"));
somethingChanged = true;
}
if (classGen.getSuperclassName().equals("java.lang.ClassLoader")) {
transformClassLoader(classGen);
somethingChanged = true;
}
if (somethingChanged) {
javaClass = classGen.getJavaClass();
}
classes.put(name.replaceAll("\\.class", ""), javaClass.getBytes());
}
}
} catch (Exception e) {
Logger.getRootLogger().error("Exception: ", e);
}
}
private void transformClassLoader(ClassGen classGen) {
String classDataClassName = ClassData.class.getCanonicalName();
String classDataFieldName = null;
for (Field field : ClassData.class.getDeclaredFields()) {
if (field.getType().equals(byte[].class)) {
classDataFieldName = field.getName();
}
}
String botPanelMethodName = null;
for (java.lang.reflect.Method m : BotPanel.class.getMethods()) {
if (Modifier.isStatic(m.getModifiers()) || m.getParameterTypes().length != 5 || !m.getParameterTypes()[4].equals(ProtectionDomain.class)) {
continue;
}
botPanelMethodName = m.getName();
}
ConstantPoolGen cpg = classGen.getConstantPool();
classGen.addField(new com.sun.org.apache.bcel.internal.classfile.Field(0, cpg.addUtf8("ourBotPanel"), cpg.addUtf8(Type.getType(BotPanel.class).getSignature()), null, cpg.getConstantPool()));
for (Method method : classGen.getMethods()) {
if (!method.getName().equals("loadClass")) {
continue;
}
MethodGen methodGen = new MethodGen(method, classGen.getClassName(), cpg);
InstructionFactory instructionFactory = new InstructionFactory(classGen);
InstructionList instructionList = methodGen.getInstructionList();
InstructionFinder instructionFinder = new InstructionFinder(instructionList);
for (Iterator<InstructionHandle[]> iterator = instructionFinder.search("aload aload aload iconst_0 aload ARRAYLENGTH aload getfield InvokeInstruction"); iterator.hasNext();) {
InstructionHandle[] ih = iterator.next();
InvokeInstruction invokeInstruction = (InvokeInstruction) ih[ih.length - 1].getInstruction();
if (!invokeInstruction.getMethodName(cpg).equals("defineClass")) {
continue;
}
InstructionList newList = new InstructionList();
newList.append(InstructionConstants.ALOAD_0);
newList.append(instructionFactory.createFieldAccess(classGen.getClassName(), "ourBotPanel", Type.getType(BotPanel.class), Constants.GETFIELD));
for (int i = 1; i < ih.length - 1; i++) {
newList.append(ih[i].getInstruction());
}
newList.append(instructionFactory.createInvoke(BotPanel.class.getCanonicalName(), botPanelMethodName, Type.getType(ClassData.class), Type.getArgumentTypes("(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)L"+ClassData.class.getCanonicalName()+";"), Constants.INVOKEVIRTUAL));
newList.append(instructionFactory.createFieldAccess(classDataClassName, classDataFieldName, Type.getType(byte[].class), Constants.GETFIELD));
ALOAD aload = (ALOAD) ih[2].getInstruction();
newList.append(new ASTORE(aload.getIndex()));
final InstructionHandle firstHandle = instructionList.insert(ih[0], newList);
if(ih[0].hasTargeters()){
for(InstructionTargeter targeter: ih[0].getTargeters()){
targeter.updateTarget(ih[0], firstHandle);
}
}
System.out.println("Injected class def change.");
}
methodGen.setMaxStack();
methodGen.setMaxLocals();
classGen.replaceMethod(method, methodGen.getMethod());
}
for (Method method : classGen.getMethods()) {
if (!method.getName().equals("<init>")) {
continue;
}
MethodGen methodGen = new MethodGen(method, classGen.getClassName(), cpg);
InstructionFactory instructionFactory = new InstructionFactory(classGen);
InstructionList instructionList = methodGen.getInstructionList();
InstructionList newList = new InstructionList();
String setClassLoaderMethodName = null;
for (java.lang.reflect.Method m : BotControl.class.getMethods()) {
if (!Modifier.isStatic(m.getModifiers()) || m.getParameterTypes().length != 1 || !m.getParameterTypes()[0].equals(ClassLoader.class)) {
continue;
}
setClassLoaderMethodName = m.getName();
}
final Type botPanelType = Type.getType(BotPanel.class);
newList.append(InstructionConstants.ALOAD_0); // putfield obgject link
newList.append(InstructionConstants.ALOAD_0); // ARG
newList.append(instructionFactory.createInvoke(BotControl.class.getCanonicalName(), setClassLoaderMethodName, botPanelType, new Type[]{Type.getType(ClassLoader.class)}, Constants.INVOKESTATIC));
newList.append(instructionFactory.createFieldAccess(classGen.getClassName(), "ourBotPanel", botPanelType, Constants.PUTFIELD));
newList.append(instructionFactory.createPrintln("Injected classloader"));
InstructionFinder instructionFinder = new InstructionFinder(instructionList);
for (Iterator<InstructionHandle[]> iterator = instructionFinder.search("invokespecial"); iterator.hasNext();) {
InstructionHandle[] ih = iterator.next();
InvokeInstruction invokeInstruction = (InvokeInstruction) ih[0].getInstruction();
if (invokeInstruction.getMethodName(cpg).equals("<init>") && invokeInstruction.getClassName(cpg).contains("ClassLoader")) {
instructionList.append(ih[0], newList);
System.out.println("Injected botPanel back link");
}
}
methodGen.setMaxStack();
methodGen.setMaxLocals();
classGen.replaceMethod(method, methodGen.getMethod());
}
}
private Permissions createPermissions() {
final Permissions ps = new Permissions();
/*ps.add(new AWTPermission("accessEventQueue"));
ps.add(new AWTPermission("createRobot"));
ps.add(new AWTPermission("fullScreenExclusive"));
ps.add(new PropertyPermission("user.home", "read"));
ps.add(new PropertyPermission("java.vendor", "read"));
ps.add(new PropertyPermission("java.version", "read"));
ps.add(new PropertyPermission("os.name", "read"));
ps.add(new PropertyPermission("os.arch", "read"));
ps.add(new PropertyPermission("os.version", "read"));
ps.add(new SocketPermission("*", "connect,resolve"));
String uDir = System.getProperty("user.home");
if (uDir != null) {
uDir += "/";
} else {
uDir = "~/";
} */
/*final String[] dirs = {"c:/rscache/", "/rscache/", "c:/windows/", "c:/winnt/",
"c:/", uDir, "/tmp/", "."};
final String[] rsDirs = {".jagex_cache_32", ".file_store_32"};
for (String dir : dirs) {
final File f = new File(dir);
ps.add(new FilePermission(dir, "read"));
if (!f.exists()) {
continue;
}
dir = f.getPath();
for (final String rsDir : rsDirs) {
ps.add(new FilePermission(dir + File.separator + rsDir + File.separator + "-", "read"));
ps.add(new FilePermission(dir + File.separator + rsDir + File.separator + "-", "write"));
}
} */
/*ps.add(new FilePermission("<<ALL FILES>>", "read,write,execute"));
ps.add(new RuntimePermission("loadLibrary.*"));
*/
ps.add(new AllPermission());
ps.setReadOnly();
return ps;
}
public final Class<?> findClass(String name) throws ClassNotFoundException {
if (classes.containsKey(name)) {
byte buffer[] = classes.remove(name);
return defineClass(name, buffer, 0, buffer.length, domain);
}
return super.loadClass(name);
}
/**
* Returns all of the <tt>Packages</tt> defined by this class loader and
* its ancestors. </p>
*
* @return The array of <tt>Package</tt> objects defined by this
* <tt>ClassLoader</tt>
* @since 1.2
*/
@Override
protected Package[] getPackages() {
System.out.println("Requested packages.");
return super.getPackages();
}
}