ByNameModding / BNM-Android

Modding il2cpp games by classes, methods, fields names on Android.
MIT License
210 stars 41 forks source link

Request for Action, UnityAction, and UnityEvent support #21

Closed solomode0001 closed 5 months ago

solomode0001 commented 9 months ago

System.Action

// Token: 0x04000B48 RID: 2888
[Token(Token = "0x4000B48")]
[FieldOffset(Offset = "0x4")]
public Action<float> OnCrystalChange;

void Update(void* instance)
{
    auto OnCrystalChange = *(Action<float>**)((uint64_t)instance + 0x4);
    if (OnCrystalChange != NULL)
    {
        OnCrystalChange.Invoke(999999.0f);
    }
}

UnityEngine.Events.UnityAction

// Token: 0x04000B48 RID: 2888
[Token(Token = "0x4000B48")]
[FieldOffset(Offset = "0x4")]
public UnityAction<float> OnCrystalChange;

void Update(void* instance)
{
    auto OnCrystalChange = *(UnityAction<float>**)((uint64_t)instance + 0x4);
    if (OnCrystalChange != NULL)
    {
        OnCrystalChange.Invoke(999999.0f);
    }
}

UnityEngine.Events.UnityEvent

// Token: 0x04000B48 RID: 2888
[Token(Token = "0x4000B48")]
[FieldOffset(Offset = "0x4")]
public UnityEvent<float> OnCrystalChange;

void Update(void* instance)
{
    auto OnCrystalChange = *(UnityEvent<float>**)((uint64_t)instance + 0x4);
    if (OnCrystalChange != NULL)
    {
        OnCrystalChange.Invoke(999999.0f);
    }
}

With parameter

// Token: 0x0400002F RID: 47
[Token(Token = "0x400002F")]
[FieldOffset(Offset = "0x4")]
public UnityAction<IAPOperationStatus, string, StoreProduct> OnCompleteMethod;

void Update(void* instance)
{
    auto OnCompleteMethod = *(UnityAction<int, monoString*, void*>**)((uint64_t)instance + 0x4);
    if (OnCompleteMethod != NULL)
    {
        auto newString = BNM::CreateMonoString("Testing");
        auto newClassStore = NULL;
        OnCompleteMethod.Invoke(0, newString, newClassStore);
    }
}
BNM-Dev commented 9 months ago

Maybe will be added in BNM v2.0

BNM-Dev commented 5 months ago

Done in BNM v2.0 beta. (I will close issue with its release). Example: C# side:

public class Delegates : MonoBehaviour { // Who cares about style in testes?
    public delegate int JustDelegate(int x, int y);
    public JustDelegate justDelegateDef;
    public UnityAction<int, int> JustUnityAction;
    public Action<int, int> JustAction;
    public UnityEvent<int, int> JustEvent;
    private AndroidJavaClass logClass;
    void Log(string s)  { logClass.CallStatic<int>("e", "BNM_TargetApp", s); }
    void Start()
    {
        logClass = new AndroidJavaClass("android.util.Log");
        justDelegateDef += delegate
            (int x, int y)
        {
            Log($"justDelegateDef(1) x: {x}, y: {y}");
            return 1;
        };
        justDelegateDef += delegate
            (int x, int y)
        {
            Log($"justDelegateDef(3) x: {x}, y: {y}");
            return 3;
        };
        justDelegateDef += delegate(int x, int y)
        {
            Log($"justDelegateDef(500) x: {x}, y: {y}");
            return 500;
        };

        JustAction += delegate
        (int x, int y)
        {
            Log($"JustAction x: {x}, y: {y}");
            return;
        };
        JustUnityAction += delegate
        (int x, int y)
        {
            Log($"JustUnityAction x: {x}, y: {y}");
            return;
        };
        JustEvent.AddListener(delegate
            (int x, int y)
        {
            Log($"JustEvent x: {x}, y: {y}");
            return;
        });
    }
}

C++ (BNM) side:

struct Delegates : BNM::UnityEngine::MonoBehaviour {
    BNM::MulticastDelegate<int> *justDelegateDef;
    BNM::UnityEngine::UnityAction<int, int> *JustUnityAction;
    BNM::Structures::Mono::Action<int, int> *JustAction;
    BNM::UnityEngine::UnityEvent<int, int> *JustEvent;
    void *logClass;

    BNM_CustomClass(Delegates, BNM::CompileTimeClassBuilder().Class("Delegates").Build(), {}, {});
    void Start() {
        BNM_CallOriginalCustomMethod(Start, this);

        BNM_LOG_DEBUG("justDelegateDef: %p", justDelegateDef);
        BNM_LOG_DEBUG("JustUnityAction: %p", JustUnityAction);
        BNM_LOG_DEBUG("JustAction: %p", JustAction);
        BNM_LOG_DEBUG("JustEvent: %p", JustEvent);
        if (justDelegateDef) justDelegateDef->Invoke(10, 60);
        if (JustUnityAction) JustUnityAction->Invoke(70, 9);
        if (JustAction) JustAction->Invoke(30, 42);
        if (JustEvent) JustEvent->Invoke(7, 234);
    }
    BNM_CustomMethod(Start, false, BNM::GetType<void>(), "Start");

};

Logs:

ByNameModding           com.DefaultCompany.TestStructs       D  justDelegateDef: 0x7986ef6900
ByNameModding           com.DefaultCompany.TestStructs       D  JustUnityAction: 0x7986ef67e0
ByNameModding           com.DefaultCompany.TestStructs       D  JustAction: 0x7986ef6870
ByNameModding           com.DefaultCompany.TestStructs       D  JustEvent: 0x7986eea480
BNM_TargetApp           com.DefaultCompany.TestStructs       E  justDelegateDef(1) x: 10, y: 60
BNM_TargetApp           com.DefaultCompany.TestStructs       E  justDelegateDef(3) x: 10, y: 60
BNM_TargetApp           com.DefaultCompany.TestStructs       E  justDelegateDef(500) x: 10, y: 60
BNM_TargetApp           com.DefaultCompany.TestStructs       E  JustUnityAction x: 70, y: 9
BNM_TargetApp           com.DefaultCompany.TestStructs       E  JustAction x: 30, y: 42
BNM_TargetApp           com.DefaultCompany.TestStructs       E  JustEvent x: 7, y: 234

Work with stripped engine code.

solomode0001 commented 5 months ago

Done in BNM v2.0 beta. (I will close issue with its release). Example: C# side:

public class Delegates : MonoBehaviour { // Who cares about style in testes?
    public delegate int JustDelegate(int x, int y);
    public JustDelegate justDelegateDef;
    public UnityAction<int, int> JustUnityAction;
    public Action<int, int> JustAction;
    public UnityEvent<int, int> JustEvent;
    private AndroidJavaClass logClass;
    void Log(string s)  { logClass.CallStatic<int>("e", "BNM_TargetApp", s); }
    void Start()
    {
        logClass = new AndroidJavaClass("android.util.Log");
        justDelegateDef += delegate
            (int x, int y)
        {
            Log($"justDelegateDef(1) x: {x}, y: {y}");
            return 1;
        };
        justDelegateDef += delegate
            (int x, int y)
        {
            Log($"justDelegateDef(3) x: {x}, y: {y}");
            return 3;
        };
        justDelegateDef += delegate(int x, int y)
        {
            Log($"justDelegateDef(500) x: {x}, y: {y}");
            return 500;
        };

        JustAction += delegate
        (int x, int y)
        {
            Log($"JustAction x: {x}, y: {y}");
            return;
        };
        JustUnityAction += delegate
        (int x, int y)
        {
            Log($"JustUnityAction x: {x}, y: {y}");
            return;
        };
        JustEvent.AddListener(delegate
            (int x, int y)
        {
            Log($"JustEvent x: {x}, y: {y}");
            return;
        });
    }
}

C++ (BNM) side:

struct Delegates : BNM::UnityEngine::MonoBehaviour {
    BNM::MulticastDelegate<int> *justDelegateDef;
    BNM::UnityEngine::UnityAction<int, int> *JustUnityAction;
    BNM::Structures::Mono::Action<int, int> *JustAction;
    BNM::UnityEngine::UnityEvent<int, int> *JustEvent;
    void *logClass;

    BNM_CustomClass(Delegates, BNM::CompileTimeClassBuilder().Class("Delegates").Build(), {}, {});
    void Start() {
        BNM_CallOriginalCustomMethod(Start, this);

        BNM_LOG_DEBUG("justDelegateDef: %p", justDelegateDef);
        BNM_LOG_DEBUG("JustUnityAction: %p", JustUnityAction);
        BNM_LOG_DEBUG("JustAction: %p", JustAction);
        BNM_LOG_DEBUG("JustEvent: %p", JustEvent);
        if (justDelegateDef) justDelegateDef->Invoke(10, 60);
        if (JustUnityAction) JustUnityAction->Invoke(70, 9);
        if (JustAction) JustAction->Invoke(30, 42);
        if (JustEvent) JustEvent->Invoke(7, 234);
    }
    BNM_CustomMethod(Start, false, BNM::GetType<void>(), "Start");

};

Logs:

ByNameModding           com.DefaultCompany.TestStructs       D  justDelegateDef: 0x7986ef6900
ByNameModding           com.DefaultCompany.TestStructs       D  JustUnityAction: 0x7986ef67e0
ByNameModding           com.DefaultCompany.TestStructs       D  JustAction: 0x7986ef6870
ByNameModding           com.DefaultCompany.TestStructs       D  JustEvent: 0x7986eea480
BNM_TargetApp           com.DefaultCompany.TestStructs       E  justDelegateDef(1) x: 10, y: 60
BNM_TargetApp           com.DefaultCompany.TestStructs       E  justDelegateDef(3) x: 10, y: 60
BNM_TargetApp           com.DefaultCompany.TestStructs       E  justDelegateDef(500) x: 10, y: 60
BNM_TargetApp           com.DefaultCompany.TestStructs       E  JustUnityAction x: 70, y: 9
BNM_TargetApp           com.DefaultCompany.TestStructs       E  JustAction x: 30, y: 42
BNM_TargetApp           com.DefaultCompany.TestStructs       E  JustEvent x: 7, y: 234

Work with stripped engine code.

Oh my gosh, I can't wait for it to be released! I hope it comes out soon. Thank you so much!

However, if I may ask for one more thing, how can we create a feature to call Java methods like this:

public class MyToast {
    public static void showToast(Context context, String message, int length) {
        Toast.makeText(context, message, length).show();
    }
}

void Start()
{
    auto unityPlayer = BNM::LoadJavaClass("com.unity3d.player.UnityPlayer");
    auto currentActivity = unityPlayer.GetStatic<void*>("currentActivity");

    auto myClass = BNM::LoadJavaClass("com.bynamemodding.MyToastClass");
    myClass.CallStatic<void>("showToast", currentActivity, "Hello From Bynamemodding");
}

If this is possible, ByNameModding will have the most complete and best features for modding

BNM-Dev commented 5 months ago

However, if I may ask for one more thing, how can we create a feature to call Java methods like this:

public class MyToast {
    public static void showToast(Context context, String message, int length) {
        Toast.makeText(context, message, length).show();
    }
}

void Start()
{
  auto unityPlayer = BNM::LoadJavaClass("com.unity3d.player.UnityPlayer");
  auto currentActivity = unityPlayer.GetStatic<void*>("currentActivity");

  auto myClass = BNM::LoadJavaClass("com.bynamemodding.MyToastClass");
  myClass.CallStatic<void>("showToast", currentActivity, "Hello From Bynamemodding");
}

If this is possible, ByNameModding will have the most complete and best features for modding

Java API don't needed because Android has JNI. And if you don't like C API, I think on github you can find C++ wrapper. BNM's main target is Unity, not JNI.


#include <jni.h>

JavaVM *globalVm{};
jclass UnityPlayer{};
jclass myClass{};

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, [[maybe_unused]] void *reserved) {
    JNIEnv *env;
    vm->GetEnv((void **) &env, JNI_VERSION_1_6);

    globalVm = vm;
    UnityPlayer = (jclass) env->NewGlobalRef(env->FindClass(OBFUSCATE_BNM("com/unity3d/player/UnityPlayer")));
    myClass = (jclass) env->NewGlobalRef(env->FindClass(OBFUSCATE_BNM("com/bynamemodding/MyToastClass")));

    return JNI_VERSION_1_6;
}
void Start() {
    JNIEnv *env;
    globalVm->GetEnv((void **) &env, JNI_VERSION_1_6);
    if (!env) globalVm->AttachCurrentThread(&env, nullptr);

    auto currentActivityId = env->GetFieldID(UnityPlayer, OBFUSCATE_BNM("currentActivity"), OBFUSCATE_BNM("Landroid/app/Activity;"));
    auto currentActivity = env->GetStaticObjectField(UnityPlayer, currentActivityId);
    auto showToast = env->GetStaticMethodID(myClass, OBFUSCATE_BNM("showToast"), OBFUSCATE_BNM("(Landroid/context/Context;Ljava/lang/String;I)V"));
    env->CallStaticVoidMethod(myClass, showToast, currentActivity, env->NewStringUTF(OBFUSCATE_BNM("Hello From ByNameModding")), 0);
}
BNM-Dev commented 5 months ago

v2.0 Beta released.