A lightweight, mixin like injection lib using ASM

Overview

ClassTransform

A lightweight, mixin like injection lib using ASM.
The usage is like Mixins. You can almost copy-paste mixins code and it works.

Why?

I wanted a lightweight version of mixins which I can easily add into any program.
It even contains a custom ClassLoader to inject into classes before loading them if you can't resort to agents.

Usage

Gradle/Maven/Jar download

To use ClassTransform with Gradle/Maven you can check out the repo on Jitpack.
If you want a jar file, you can also download it from Jitpack:
https://jitpack.io/com/github/Lenni0451/ClassTransform/<version>/ClassTransform-<version>.jar
As an example: For version cf1f3f88ea this results in the following link:
https://jitpack.io/com/github/Lenni0451/ClassTransform/cf1f3f88ea/ClassTransform-cf1f3f88ea.jar

Transformer Manager

The TransformerManager is the main class which handles the entire injection process.
When creating a new TransformerManager you have to provide a IClassProvider and optionally a AMapper.
The IClassProvider is used to get the bytecode of classes if needed for e.g. frame computation.
The AMapper can provide mappings which get automatically applied to the transformers to allow injection obfuscated code.

Transformer types

There are different types of transformers available:

Transformer Type Description
IBytecodeTransformer The IBytecodeTransformer can modify the bytecode of every class before applying any other transformer
IRawTransformer The IRawTransformer gets access to the ClassNode before the default transformers are applied
Default Transformer The default transformers are the ones you know from Mixins
IPostTransformer The IPostTransformer gets the modified output bytecode. Mainly used for dumping classes if something doesn't work

Annotations

The following injection annotations are available:

Annotation Type Description
CASM The access to the ClassNode of the entire class or a MethodNode of the wanted method
CInject Inject into any method at the given targets
CModifyConstant Modify a constant value in a method (null/int/long/float/double/string)
COverride Override any method in the target class
CRedirect Redirect a method call, a field get/put or new object to your injection method
CWrapCatch Wrap a try-catch block around the entire method and handle the exception

The following util annotations are available:

Annotation Type Description
CShadow Create a shadow of a field to access it in the transformer class
CSlice Choose any target in a method to create a slice to allow more precise injections
CTarget Choose the target to inject to
CTransformer Mark a class as a transformer and define the injected classes

Injecting using an Agent

Using an Agent is the preferred way to inject code using ClassTransform. You can easily hook the Instrumentation by calling the hookInstrumentation method. Example:

public static void agentmain(String args, Instrumentation instrumentation) throws Throwable {
    TransformerManager transformerManager = new TransformerManager(new BasicClassProvider());
    transformerManager.addTransformer("net.lenni0451.classtransform.TestTransformer");
    transformerManager.hookInstrumentation(instrumentation);
}

The class net.lenni0451.classtransform.TestTransformer in this example is our default transformer.

Injecting using a ClassLoader

ClassTransform also provides the InjectionClassLoader to allow injection into classes without transformation access.
When creating a new InjectionClassLoader you have to provide a TransformerManager and an URL[] array with the classpath.
Optionally a parent ClassLoader can be provided.
Example:

public static void main(String[] args) throws Throwable {
    TransformerManager transformerManager = new TransformerManager(new BasicClassProvider());
    transformerManager.addTransformer("net.lenni0451.classtransform.TestTransformer");
    InjectionClassLoader classLoader = new InjectionClassLoader(transformerManager, Launcher.class.getProtectionDomain().getCodeSource().getLocation());
    classLoader.executeMain("net.lenni0451.classtransform.Main", "main", args);
}

You can add resources to the ClassLoader by using the addRuntimeResource method.
Example:

classLoader.addRuntimeResource("test", new byte[123]);

This also works with (runtime generated) classes.

Default Transformer

The default transformers are annotated with @CTransformer.
As the arguments of the annotation you can pass a class array and/or a String array with class names.
Example:

@CTransformer(Example.class)
public class TestTransformer {

Annotation Examples

CASM

With the CASM annotation you can access the ClassNode of the entire class or a MethodNode of the wanted method.
Example:

@CASM
public void test(ClassNode classNode) {
    System.out.println(classNode.name);
}

@CASM("toString")
public void test(MethodNode methodNode) {
    System.out.println(methodNode.name);
}

CInject

With the CInject annotation you can inject into any method at the given targets.
Example:

@CInject(method = "equals(Ljava/lang/Object;)Z",
        target = @CTarget(
                value = "THROW",
                shift = CTarget.Shift.BEFORE,
                ordinal = 1
        ),
        slice = @CSlice(
                from = @CTarget(value = "INVOKE", target = "Ljava/util/Objects;equals(Ljava/lang/Object;Ljava/lang/Object;)Z")
        ),
        cancellable = true)
public void test(Object other, InjectionCallback callback) {
    System.out.println("Checking equals: " + other);
}

This example method injects into the equals method of the given class.
It injects a method call above the second throw instruction after the first Objects#equals call.
The called method is your injection method, and you can cancel the rest of the original method by using the InjectionCallback.
In this case the equals method must return a boolean. So you have to call InjectionCallback#setReturnValue with a boolean.
If your inject target is RETURN/TAIL/THROW you can use InjectionCallback#getReturnValue to get the current return value/thrown exception.

CModifyConstant

With the CModifyConstant annotation you can modify a constant value in a method (null/int/long/float/double/string).
Example:

@CModifyConstant(
        method = "log",
        stringValue = "[INFO]")
public String infoToFatal(String originalConstant) {
    return "[FATAL]";
}

This example method modifies the string constant value of the log method.
The method is called with the original constant value as argument and must return the new constant value.
Only one constant value can be modified at a time.

COverride

With the COverride annotation you can override any method in the target class.
Example:

@COverride
public String toString() {
    return "Test";
}

This example method overrides the toString method of the given class.
The arguments and return type of the overridden method need to be the same.
You can also set the target method name as a parameter in the annotation.

CRedirect

With the CRedirect annotation you can redirect a method call, a field get/put or new object to your injection method. Example:

@CRedirect(
        method = "getNewRandom",
        target = @CTarget(
                value = "NEW",
                target = "java/util/Random"
        ),
        slice = @CSlice(
                to = @CTarget("RETURN")
        ))
public Random makeSecure() {
    return new SecureRandom();
}

This example method redirects the new Random() call to the makeSecure method.
This replaces the original Random instance with a Secure Random.

CWrapCatch

With the CWrapCatch annotation you can wrap a try-catch block around the entire method and handle the exception.

@CWrapCatch("getResponseCode")
public int dontThrow(IOException e) {
    return 404;
}

This example method wraps the getResponseCode method in a try-catch block and handles the exception.
The exception in the parameter is the exception catched by the try-catch block.
The return type must be the one of the original method.
You can even re-throw the exception or throw a new one.

You might also like...

Lattice is a powerful, lightweight business extension invoke framework. By using the Lattice framework, complex business customization can be efficiently organized and managed.

Lattice Framework Introduction Lattice is a powerful, lightweight business extension invoke framework. By using the Lattice framework, complex busines

Dec 30, 2022

Provides some Apple Wallet functionality, like adding passes, removing passes and checking passises for existing.

Provides some Apple Wallet functionality, like adding passes, removing passes and checking passises for existing.

react-native-wallet-manager Provides some Apple Wallet's functionality, like adding passes, removing passes and checking passises for existing. Instal

Nov 12, 2022

An TikTok-like app

An TikTok-like app

Video-app An TikTok likes app Middleware and db all tools below installed by docker please make sure you have installed docker in your machine tool ve

Dec 26, 2021

SurvivalCore featuring SMP features like claims and land protection for Nukkit!

SurvivalCore SurvivalCore featuring Survival characterstics and claims and land protection for Nukkit! Features: Claims System : Use /claim or /c to c

Jan 4, 2022

Rails like error pages for Spring Boot applications that are only active in development.

Rails like error pages for Spring Boot applications that are only active in development.

Better Error Pages Table of Contents Introduction Demo Quick Start Configuration Limitations License Introduction This is a Spring Boot Starter projec

Jan 2, 2022

The combined power of JUnit, Guice and Mockito. Plus it sounds like a cool martial art.

The combined power of JUnit, Guice and Mockito. Plus it sounds like a cool martial art.

The combined power of JUnit, Guice and Mockito. Plus it sounds like a cool martial art. So you started using dependency injection because somebody tol

Sep 19, 2022

An open source civilization like game made in LibGDX

An open source civilization like game made in LibGDX

OpenCiv A turn based strategy game built on LibGDX, with a rouge-like tile set. About OpenCiv is a love letter to turn based strategy games inspired b

Dec 26, 2022

Twitter like web application

Sweater Twitter like web application Read Me First The following was discovered as part of building this project: The original package name 'io.github

Feb 1, 2022

CRUD about create, update and delete items like fish and fishbowl

CRUD about create, update and delete items like fish and fishbowl

CreacionPecesYPeceras Este repositorio contiene el código fuente y la base de datos (fichero .sql) que conforman una aplicación CRUD (Create, Read, Up

Mar 2, 2022
Comments
  • Return null from agent transformer

    Return null from agent transformer

    Agent transformers are supposed to return null when no changes occur. This helps the agent machinery determine if the class actually needs to be updated.

    opened by Gaming32 0
Owner
Lenni0451
Lenni0451
MockNeat - the modern faker lib.

Mockneat is an arbitrary data-generator open-source library written in Java. It provides a simple but powerful (fluent) API that enables developers to

Andrei Ciobanu 506 Dec 26, 2022
simple ui utilities lib

UiUtilities a custom android libe help to show or hide view as expande row Step 1. Add the JitPack repository to your build file Add it in your root b

Ali Tarek Hrhera 3 Apr 18, 2022
A small companion library to Mixin, designed to help you write your Mixins in a more expressive and compatible way.

MixinExtras A small companion library to Mixin, designed to help you write your Mixins in a more expressive and compatible way. More information about

null 97 Jan 7, 2023
A Mixin framework for Spigot/Bukkit that allows you to hook custom event anywhere

A Mixin framework for Spigot/Bukkit that allows you to hook custom event anywhere. Start coding your advanced plugins today!

DragonCommissions 14 Nov 30, 2022
The Mixin re-mapper for Lunar Client.

LunarRemapper The Mixin re-mapper for Lunar Client. I have little time to work on this project, if you know what you're doing and have familiarized yo

null 45 Nov 28, 2022
Spring Kurulumundan Başlayarak, Spring IOC ve Dependency Injection, Hibernate, Maven ve Spring Boot Konularına Giriş Yapıyoruz.

Spring Tutorial for Beginners File Directory Apache Tomcat Apache Tomcat - Eclipse Bağlantısı Spring Paketlerinin İndirilmesi ve Projeye Entegrasyonu

İbrahim Can Erdoğan 11 Apr 11, 2022
A light-weight and dynamic dependency injection framework

⚠️ This project is now part of the EE4J initiative. This repository has been archived as all activities are now happening in the corresponding Eclipse

Java EE 105 Dec 23, 2022
Querystream - Build JPA Criteria queries using a Stream-like API

QueryStream QueryStream allows you to perform JPA queries using a Stream-like API. Just like a Java 8 Stream, a QueryStream is built up in a pipeline,

null 11 Sep 24, 2022
PortalController - A rudimentary TeamViewer-like remote control app for Android, using ws.

PortalController A TeamViewer-like app for Android-to-Android remote control, using node.js and websockets (ws). Some insight The reason I call it rud

Mike Anderson 10 Dec 15, 2022
The goal of the project is to create a web application using Java EE and database (PostgreSQL) without connecting a modern technology stack like spring boot and hibernate

About The Project SignIn page SignUp page Profile page The goal of the project is to create a web application using Java EE and database (PostgreSQL)

Islam Khabibullin 2 Mar 23, 2022