LSPosed / LSPlant

A hook framework for Android Runtime (ART)
https://lsposed.org/LSPlant/
GNU Lesser General Public License v3.0
890 stars 220 forks source link

MakeClassInheritable doesn't remove final modifier from class for class linker #41

Closed Juby210 closed 1 year ago

Juby210 commented 1 year ago

Hello! I found an issue with MakeClassInheritable, it removes final modifier only for reflection, while trying to extend modified class fails with java.lang.VerifyError

Here's my example code: Test.java

package io.github.juby210.lsplant_demo;

public class Test extends android.app.GameState {
    public Test() {
        super(false, 0);
    }
}

AndroidTest.java

@RunWith(AndroidJUnit4.class)
public class AndroidTest {
    @Test
    public void makeInheritable() {
        var clazz = GameState.class;
        assertEquals(Modifier.FINAL, clazz.getModifiers() & Modifier.FINAL);

        assertTrue(Main.makeInheritable(clazz));
        assertEquals(0, clazz.getModifiers() & Modifier.FINAL);

        Log.d("TestRunner", "all tests ok");

        new io.github.juby210.lsplant_demo.Test();
    }
}

It passes all assert tests, but throws VerifyError on creation of new instance of my Test class. Here's full demo repo: https://github.com/Juby210/lsplant_demo

studio64_sN7do4OPZF

canyie commented 1 year ago

I think this is because ART pre-verified your subclass before MakeClassInheritable is actually called. Try to use ClassLoader to manually load your class after calling MakeClassInheritable

Juby210 commented 1 year ago

oh, yeah using

        var c = AndroidTest.class.getClassLoader().loadClass("io.github.juby210.lsplant_demo.Test");
        c.newInstance();

works Do you think that using class like this without reflection is possible?

yujincheng08 commented 1 year ago

You can try Test.class.newInstance(), which should do the same trick.

You can bypass background verification by hooking _ZN3art14OatFileManager25RunBackgroundVerificationERKNSt3__16vectorIPKNS_7DexFileENS1_9allocatorIS5_EEEEP8_jobjectPKc.

We may consider introducing a new API to discard the verification result of a specific class (in your case, the Test class).

Juby210 commented 1 year ago

Thanks for the help, in the meantime I also found that if I start using the Test class in a class that's loaded after calling makeInheritable then it also works without using reflection image

yujincheng08 commented 1 year ago

Anyway, I will close this issue because MakeClassInheritable does work.