ByteCodePatcher
Fix the bungle that was my last merge. Refactored class/method names to adhere to ByteCodePatcher layout Import all transformers
This commit is contained in:
@@ -86,245 +86,6 @@ public class ClientLoader
|
|||||||
this.clientConfigLoader = clientConfigLoader;
|
this.clientConfigLoader = clientConfigLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Applet load()
|
|
||||||
{
|
|
||||||
if (updateCheckMode == NONE)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File injectedClientFile = ByteCodeUtils.injectedClientFile;
|
|
||||||
File hijackedClientFile = ByteCodeUtils.hijackedClientFile;
|
|
||||||
Manifest manifest = new Manifest();
|
|
||||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
|
||||||
target = new JarOutputStream(new FileOutputStream(injectedClientFile), manifest);
|
|
||||||
RSConfig config = clientConfigLoader.fetch();
|
|
||||||
|
|
||||||
Map<String, byte[]> zipFile = new HashMap<>();
|
|
||||||
{
|
|
||||||
Certificate[] jagexCertificateChain = getJagexCertificateChain();
|
|
||||||
String codebase = config.getCodeBase();
|
|
||||||
String initialJar = config.getInitialJar();
|
|
||||||
URL url = new URL(codebase + initialJar);
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(url)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
|
||||||
{
|
|
||||||
JarInputStream jis;
|
|
||||||
|
|
||||||
jis = new JarInputStream(response.body().byteStream());
|
|
||||||
|
|
||||||
byte[] tmp = new byte[4096];
|
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream(756 * 1024);
|
|
||||||
for (; ; )
|
|
||||||
{
|
|
||||||
JarEntry metadata = jis.getNextJarEntry();
|
|
||||||
if (metadata == null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.reset();
|
|
||||||
for (; ; )
|
|
||||||
{
|
|
||||||
int n = jis.read(tmp);
|
|
||||||
if (n <= -1)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buffer.write(tmp, 0, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
zipFile.put(metadata.getName(), buffer.toByteArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateCheckMode == AUTO)
|
|
||||||
{
|
|
||||||
Map<String, String> hashes;
|
|
||||||
try (InputStream is = ClientLoader.class.getResourceAsStream("/patch/hashes.json"))
|
|
||||||
{
|
|
||||||
hashes = new Gson().fromJson(new InputStreamReader(is), new TypeToken<HashMap<String, String>>()
|
|
||||||
{
|
|
||||||
}.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<String, String> file : hashes.entrySet())
|
|
||||||
{
|
|
||||||
byte[] bytes = zipFile.get(file.getKey());
|
|
||||||
|
|
||||||
String ourHash = null;
|
|
||||||
if (bytes != null)
|
|
||||||
{
|
|
||||||
ourHash = Hashing.sha512().hashBytes(bytes).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.getValue().equals(ourHash))
|
|
||||||
{
|
|
||||||
if (hijackedClientFile.exists()) {
|
|
||||||
Logger.getAnonymousLogger().warning("[RuneLit] Hash checking / Client patching skipped due to hijacked client.");
|
|
||||||
updateCheckMode = VANILLA;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
log.info("{} had a hash mismatch; falling back to vanilla. {} != {}", file.getKey(), file.getValue(), ourHash);
|
|
||||||
log.info("Client is outdated!");
|
|
||||||
updateCheckMode = VANILLA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateCheckMode == AUTO)
|
|
||||||
{
|
|
||||||
ByteArrayOutputStream patchOs = new ByteArrayOutputStream(756 * 1024);
|
|
||||||
int patchCount = 0;
|
|
||||||
|
|
||||||
for (Map.Entry<String, byte[]> file : zipFile.entrySet())
|
|
||||||
{
|
|
||||||
byte[] bytes;
|
|
||||||
try (InputStream is = ClientLoader.class.getResourceAsStream("/patch/" + file.getKey() + ".bs"))
|
|
||||||
{
|
|
||||||
if (is == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = ByteStreams.toByteArray(is);
|
|
||||||
}
|
|
||||||
|
|
||||||
patchOs.reset();
|
|
||||||
Patch.patch(file.getValue(), bytes, patchOs);
|
|
||||||
file.setValue(patchOs.toByteArray());
|
|
||||||
|
|
||||||
++patchCount;
|
|
||||||
|
|
||||||
if (!file.getKey().startsWith("META")) {
|
|
||||||
add(file.getValue(), file.getKey(), target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (target!=null)
|
|
||||||
target.close();
|
|
||||||
|
|
||||||
log.info("Patched {} classes", patchCount);
|
|
||||||
}
|
|
||||||
if (hooksFile.exists()) {
|
|
||||||
ByteCodePatcher.classPool = new ClassPool(true);
|
|
||||||
ByteCodePatcher.classPool.appendClassPath(RuneLite.RUNELITE_DIR+"/injectedClient-"+ RuneLiteAPI.getVersion() +"-.jar");
|
|
||||||
Gson gson = new Gson();
|
|
||||||
Hooks hooks = gson.fromJson(new BufferedReader(new FileReader(hooksFile)), Hooks.class);
|
|
||||||
|
|
||||||
if (hooks == null && !scrapedHooks) {
|
|
||||||
System.out.println("[RuneLit] Bad hooks, re-scraping.");
|
|
||||||
ByteCodePatcher.clientInstance = initVanillaInjected(ByteCodeUtils.injectedClientFile.getPath());
|
|
||||||
ByteCodePatcher.findHooks(injectedClientFile.getPath());
|
|
||||||
scrapedHooks=true;
|
|
||||||
}
|
|
||||||
if ((hooks.clientInstance.equals("")||
|
|
||||||
hooks.projectileClass.equals("") ||
|
|
||||||
hooks.actorClass.equals("") ||
|
|
||||||
hooks.playerClass.equals("")) && !scrapedHooks) {
|
|
||||||
System.out.println("[RuneLit] Bad hooks, re-scraping.");
|
|
||||||
ByteCodePatcher.clientInstance = initVanillaInjected(ByteCodeUtils.injectedClientFile.getPath());
|
|
||||||
ByteCodePatcher.findHooks(injectedClientFile.getPath());
|
|
||||||
scrapedHooks=true;
|
|
||||||
|
|
||||||
} else if (!scrapedHooks) {
|
|
||||||
ByteCodePatcher.clientInstance = hooks.clientInstance;
|
|
||||||
ByteCodePatcher.applyHooks(ByteCodeUtils.injectedClientFile, hooks);
|
|
||||||
System.out.println("[RuneLit] Loaded hooks");
|
|
||||||
scrapedHooks=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
System.out.println("[RuneLit] Hooks file not found, scraping hooks.");
|
|
||||||
ByteCodePatcher.clientInstance = initVanillaInjected(ByteCodeUtils.injectedClientFile.getPath());
|
|
||||||
ByteCodePatcher.findHooks(injectedClientFile.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, byte[]> zipFile2 = new HashMap<>();
|
|
||||||
JarInputStream jis = new JarInputStream(new FileInputStream(hijackedClientFile));
|
|
||||||
|
|
||||||
byte[] tmp = new byte[4096];
|
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream(756 * 1024);
|
|
||||||
for (; ; ) {
|
|
||||||
JarEntry metadata = jis.getNextJarEntry();
|
|
||||||
if (metadata == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.reset();
|
|
||||||
for (; ; ) {
|
|
||||||
int n = jis.read(tmp);
|
|
||||||
if (n <= -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buffer.write(tmp, 0, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
zipFile2.put(metadata.getName(), buffer.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
String initialClass = config.getInitialClass();
|
|
||||||
|
|
||||||
ClassLoader rsClassLoader = new ClassLoader(ClientLoader.class.getClassLoader())
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
protected Class<?> findClass(String name) throws ClassNotFoundException
|
|
||||||
{
|
|
||||||
String path = name.replace('.', '/').concat(".class");
|
|
||||||
byte[] data = zipFile2.get(path);
|
|
||||||
if (data == null)
|
|
||||||
{
|
|
||||||
throw new ClassNotFoundException(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return defineClass(name, data, 0, data.length);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Class<?> clientClass = rsClassLoader.loadClass(initialClass);
|
|
||||||
|
|
||||||
Applet rs = (Applet) clientClass.newInstance();
|
|
||||||
rs.setStub(new RSAppletStub(config));
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException
|
|
||||||
| CompressorException | InvalidHeaderException | CertificateException | SecurityException e)
|
|
||||||
{
|
|
||||||
if (e instanceof ClassNotFoundException)
|
|
||||||
{
|
|
||||||
log.error("Unable to load client - class not found. This means you"
|
|
||||||
+ " are not running RuneLite with Maven as the client patch"
|
|
||||||
+ " is not in your classpath.");
|
|
||||||
}
|
|
||||||
|
|
||||||
log.error("Error loading RS!", e);
|
|
||||||
return null;
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add(byte[] bytes, String entryName ,JarOutputStream target) throws IOException {
|
|
||||||
BufferedInputStream in = null;
|
|
||||||
try {
|
|
||||||
JarEntry entry = new JarEntry(entryName);
|
|
||||||
target.putNextEntry(entry);
|
|
||||||
target.write(bytes);
|
|
||||||
target.closeEntry();
|
|
||||||
} finally {
|
|
||||||
if (in != null)
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Certificate[] getJagexCertificateChain() throws CertificateException
|
private static Certificate[] getJagexCertificateChain() throws CertificateException
|
||||||
{
|
{
|
||||||
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
||||||
@@ -524,10 +285,10 @@ public class ClientLoader
|
|||||||
if (hooks.clientInstance.equals("") ||
|
if (hooks.clientInstance.equals("") ||
|
||||||
hooks.projectileClass.equals("") ||
|
hooks.projectileClass.equals("") ||
|
||||||
hooks.actorClass.equals("") ||
|
hooks.actorClass.equals("") ||
|
||||||
hooks.mainClientInstance.equals("") ||
|
hooks.clientInstance.equals("") ||
|
||||||
hooks.playerClass.equals("")) {
|
hooks.playerClass.equals("")) {
|
||||||
System.out.println("[RuneLit] Bad hooks, re-scraping.");
|
System.out.println("[RuneLit] Bad hooks, re-scraping.");
|
||||||
ByteCodePatcher.clientInstance = getClientInstance(ByteCodeUtils.injectedClientFile.getPath());
|
ByteCodePatcher.clientInstance = ByteCodeUtils.injectedClientFile.getPath();
|
||||||
ByteCodePatcher.findHooks(injectedClientFile.getPath());
|
ByteCodePatcher.findHooks(injectedClientFile.getPath());
|
||||||
} else {
|
} else {
|
||||||
ByteCodePatcher.clientInstance = hooks.clientInstance;
|
ByteCodePatcher.clientInstance = hooks.clientInstance;
|
||||||
@@ -537,7 +298,7 @@ public class ClientLoader
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
System.out.println("[RuneLit] Hooks file not found, scraping hooks.");
|
System.out.println("[RuneLit] Hooks file not found, scraping hooks.");
|
||||||
ByteCodePatcher.clientInstance = getClientInstance(ByteCodeUtils.injectedClientFile.getPath());
|
ByteCodePatcher.clientInstance = initVanillaInjected(ByteCodeUtils.injectedClientFile.getPath());
|
||||||
ByteCodePatcher.findHooks(injectedClientFile.getPath());
|
ByteCodePatcher.findHooks(injectedClientFile.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ import javassist.CtClass;
|
|||||||
import javassist.NotFoundException;
|
import javassist.NotFoundException;
|
||||||
import net.runelite.client.RuneLite;
|
import net.runelite.client.RuneLite;
|
||||||
import net.runelite.client.rs.ClientLoader;
|
import net.runelite.client.rs.ClientLoader;
|
||||||
import net.runelite.client.rs.bytecode.transformers.ActorTransform;
|
import net.runelite.client.rs.bytecode.transformers.*;
|
||||||
import net.runelite.client.rs.bytecode.transformers.PlayerTransform;
|
|
||||||
import net.runelite.client.rs.bytecode.transformers.ProjectileTransform;
|
|
||||||
import net.runelite.client.rs.bytecode.transformers.getProjectileTransform;
|
|
||||||
import net.runelite.http.api.RuneLiteAPI;
|
import net.runelite.http.api.RuneLiteAPI;
|
||||||
import org.xeustechnologies.jcl.JarClassLoader;
|
import org.xeustechnologies.jcl.JarClassLoader;
|
||||||
|
|
||||||
@@ -45,6 +42,8 @@ public class ByteCodePatcher {
|
|||||||
transformProjectile(projectileClass);
|
transformProjectile(projectileClass);
|
||||||
Class playerClass = Class.forName(hooks.playerClass, false, child);
|
Class playerClass = Class.forName(hooks.playerClass, false, child);
|
||||||
transformPlayer(playerClass);
|
transformPlayer(playerClass);
|
||||||
|
Class clientClass = Class.forName(hooks.clientClass, false, child);
|
||||||
|
transformClient(clientClass);
|
||||||
ByteCodeUtils.updateHijackedJar();
|
ByteCodeUtils.updateHijackedJar();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -99,6 +98,7 @@ public class ByteCodePatcher {
|
|||||||
checkActor(classToLoad);
|
checkActor(classToLoad);
|
||||||
checkProjectile(classToLoad);
|
checkProjectile(classToLoad);
|
||||||
checkPlayer(classToLoad);
|
checkPlayer(classToLoad);
|
||||||
|
checkClient(classToLoad);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -153,42 +153,6 @@ public class ByteCodePatcher {
|
|||||||
pt.modify(projectile);
|
pt.modify(projectile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkPlayer(Class current) {
|
|
||||||
try {
|
|
||||||
Method method = current.getDeclaredMethod("getSkullIcon");
|
|
||||||
if (method!=null) {
|
|
||||||
hooks.playerClass = current.getName();
|
|
||||||
System.out.println("[RuneLit] Transforming Player at class: "+current.getName());
|
|
||||||
PlayerTransform pt = new PlayerTransform();
|
|
||||||
pt.modify(current);
|
|
||||||
|
|
||||||
public static void checkgetProjectiles(Class getprojectile) {
|
|
||||||
try {
|
|
||||||
Method method = getprojectile.getDeclaredMethod("getProjectiles");
|
|
||||||
if (method != null) {
|
|
||||||
hooks.mainClientInstance = getprojectile.getName();
|
|
||||||
System.out.println("[RuneLit] Transforming Projectile at class: " + getprojectile.getName());
|
|
||||||
getProjectileTransform gpt = new getProjectileTransform();
|
|
||||||
gpt.modify(getprojectile);
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
//e.printStackTrace();
|
|
||||||
} catch (NoClassDefFoundError e) {
|
|
||||||
//e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void transformPlayer(Class player) {
|
|
||||||
System.out.println("[RuneLit] Transforming Player at class: "+player.getName());
|
|
||||||
PlayerTransform pt = new PlayerTransform();
|
|
||||||
pt.modify(player);
|
|
||||||
|
|
||||||
public static void transformGetProjectile(Class current) {
|
|
||||||
System.out.println("[RuneLit] Transforming getProjectile at class: " + current.getName());
|
|
||||||
getProjectileTransform gpt = new getProjectileTransform();
|
|
||||||
gpt.modify(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkPlayer(Class current) {
|
public static void checkPlayer(Class current) {
|
||||||
try {
|
try {
|
||||||
Method method = current.getDeclaredMethod("getSkullIcon");
|
Method method = current.getDeclaredMethod("getSkullIcon");
|
||||||
@@ -198,6 +162,20 @@ public class ByteCodePatcher {
|
|||||||
PlayerTransform pt = new PlayerTransform();
|
PlayerTransform pt = new PlayerTransform();
|
||||||
pt.modify(current);
|
pt.modify(current);
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkClient(Class current) {
|
||||||
|
try {
|
||||||
|
Method method = current.getDeclaredMethod("getProjectiles");
|
||||||
|
if (method != null) {
|
||||||
|
hooks.clientInstance = current.getName();
|
||||||
|
System.out.println("[RuneLit] Transforming Projectile at class: " + current.getName());
|
||||||
|
ClientTransform ct = new ClientTransform();
|
||||||
|
ct.modify(current);
|
||||||
|
}
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
//e.printStackTrace();
|
//e.printStackTrace();
|
||||||
} catch (NoClassDefFoundError e) {
|
} catch (NoClassDefFoundError e) {
|
||||||
@@ -205,6 +183,12 @@ public class ByteCodePatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void transformClient(Class client) {
|
||||||
|
System.out.println("[RuneLit] Transforming Client at class: " + client.getName());
|
||||||
|
ClientTransform ct = new ClientTransform();
|
||||||
|
ct.modify(client);
|
||||||
|
}
|
||||||
|
|
||||||
public static void transformPlayer(Class player) {
|
public static void transformPlayer(Class player) {
|
||||||
System.out.println("[RuneLit] Transforming Player at class: " + player.getName());
|
System.out.println("[RuneLit] Transforming Player at class: " + player.getName());
|
||||||
PlayerTransform pt = new PlayerTransform();
|
PlayerTransform pt = new PlayerTransform();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public class Hooks {
|
|||||||
public String actorClass = "";
|
public String actorClass = "";
|
||||||
public String projectileClass = "";
|
public String projectileClass = "";
|
||||||
public String playerClass = "";
|
public String playerClass = "";
|
||||||
|
public String clientClass = "client"; //Always named client
|
||||||
public String[] protectedMethods;
|
public String[] protectedMethods;
|
||||||
|
|
||||||
public Hooks() {
|
public Hooks() {
|
||||||
|
|||||||
@@ -3,27 +3,26 @@ package net.runelite.client.rs.bytecode.transformers;
|
|||||||
import javassist.CtClass;
|
import javassist.CtClass;
|
||||||
import javassist.CtMethod;
|
import javassist.CtMethod;
|
||||||
import javassist.CtNewMethod;
|
import javassist.CtNewMethod;
|
||||||
|
import javassist.NotFoundException;
|
||||||
import javassist.bytecode.AnnotationsAttribute;
|
import javassist.bytecode.AnnotationsAttribute;
|
||||||
import javassist.bytecode.ClassFile;
|
import javassist.bytecode.ClassFile;
|
||||||
import javassist.bytecode.ConstPool;
|
import javassist.bytecode.ConstPool;
|
||||||
import net.runelite.client.rs.bytecode.ByteCodePatcher;
|
import net.runelite.client.rs.bytecode.ByteCodePatcher;
|
||||||
|
|
||||||
|
public class ClientTransform {
|
||||||
public class getProjectileTransform {
|
|
||||||
public CtClass ct = null;
|
public CtClass ct = null;
|
||||||
|
|
||||||
public void modify(Class getprojectile) {
|
public void modify(Class client) {
|
||||||
try {
|
try {
|
||||||
ct = ByteCodePatcher.classPool.get(getprojectile.getName());
|
ct = ByteCodePatcher.classPool.get(client.getName());
|
||||||
|
transformGetProjectiles();
|
||||||
transformGetProjectile();
|
|
||||||
ByteCodePatcher.modifiedClasses.add(ct);
|
ByteCodePatcher.modifiedClasses.add(ct);
|
||||||
} catch (Exception e) {
|
} catch (NotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void transformGetProjectile() {
|
public void transformGetProjectiles() {
|
||||||
|
|
||||||
CtMethod getProjectiles;
|
CtMethod getProjectiles;
|
||||||
try {
|
try {
|
||||||
@@ -53,4 +52,5 @@ public class getProjectileTransform {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user