Another request to complete the 'class rewrite' feature
gurt-il opened this issue · comments
Hi Lanchon,
First, I very appreciate your great job. You've saved me tons of time until now and for this, I'm thanking you.
Now to my suggestion. I've started to use the new 'class rewrite' feature and it's was really helpful. The only problem that left is when I'm trying to wrap some function, that in his signature I have the renamed class.
For example, I've renamed the class aa
to class1
.
Then I'm trying to wrap the function void function1(class1 input)
but now I actually need to wrap the function void function1(aa input)
because otherwise, I'm getting the error
Error:type 'com.testapp.class2': target 'com.testapp.bb': virtual method 'function1(com.testapp.class1):void': target 'a': (class2.java:27): target not found
So my request is when you checking for signatures for wrapping functions and etc., test the functions against the renamed names instead of the original names.
Thanks
hi,
thanks! sorry i can't really follow this up based on an imprecise english language description.
could you please post the exact, complete, tagged class and method definitions in the patch, and the error generated? their body is not needed. thanks again!
OK
So this is the smali code of class aa:
.class public final synthetic Lcom/testapp/aa;
.super Ljava/lang/Object;
.source ""
# interfaces
.implements Landroid/content/DialogInterface$OnClickListener;
# direct methods
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
# virtual methods
.method public final onClick(Landroid/content/DialogInterface;I)V
.locals 0
.annotation runtime Ljava/lang/invoke/LambdaForm$Hidden;
.end annotation
invoke-virtual {p0}, Lcom/testapp/ax;->j()V
return-void
.end method
And this is the smali code of class bb:
.class public final synthetic Lcom/testapp/bb;
.super Ljava/lang/Object;
.source ""
# instance fields
.field private final a:Lcom/testapp/aa;
# direct methods
.method public constructor <init>(Lcom/testapp/aa;)V
.locals 0
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
iput-object p1, p0, Lcom/testapp/bb;->a:Lcom/testapp/aa;
return-void
.end method
# virtual methods
.method public final a(Lcom/testapp/aa;)V
.locals 0
.annotation runtime Ljava/lang/invoke/LambdaForm$Hidden;
.end annotation
iput-object p1, p0, Lcom/testapp/bb;->a:Lcom/testapp/aa;
return-void
.end method
And this is the java code for class1
package com.testapp;
import lanchon.dexpatcher.annotation.DexAdd;
import lanchon.dexpatcher.annotation.DexEdit;
import lanchon.dexpatcher.annotation.DexIgnore;
import lanchon.dexpatcher.annotation.DexReplace;
@DexEdit(target = "aa", contentOnly = true)
public class class1 {
@DexIgnore
public class1() {
}
@DexReplace
public InputStream b(boolean paramBoolean) {
return null;
}
}
And this is the java code for class2
package com.testapp;
import lanchon.dexpatcher.annotation.DexAdd;
import lanchon.dexpatcher.annotation.DexEdit;
import lanchon.dexpatcher.annotation.DexIgnore;
import lanchon.dexpatcher.annotation.DexReplace;
@DexEdit(target = "bb", contentOnly = true)
public class class2 {
@DexIgnore
public class2() {
}
@DexWrap(target = "a")
public final void function1(class1 input) {
function1(input);
Log.d("debug", "on function1");
}
}
And the error is
Error:type 'com.testapp.class2': target 'com.testapp.bb': virtual method 'function1(com.testapp.class1):void': target 'a': (class2.java:27): target not found
Hope this will be enough
Thanks
oh i see now, you had 2 classes! i thought there was a bug in dxp!! :)
if you did this it would work as you want:
@DexEdit(target = "bb", contentOnly = true)
public class class2 {
/*
// THIS IS NOT NEEDED ANYMORE
@DexIgnore
public class2() {
}
*/
@DexWrap(target = "a")
public final void function1(class2 input) {
function1(input);
Log.d("debug", "on function1");
}
}
but no, class rewrite only rewrites the class being operated upon, and not the whole dex. there are design and implementation issues of why this is this way. imagine rewriting a 'source' class to rename it: if i rewrote the whole source dex, what is there to gain by the rename, if the clients in the source are going to look for the new name?
and the other way around, your case: what if the class has more than one target? i know this can't happen now, but it could in the future. until recently you couldn't target anonymous inner classes. now you can, but using an ugly name such as target="SomeClass$0"
, which is horrible because the 0
part might change with any recompile of the source.
imagine instead using target="SomeClass/java.lang.Runnable"
, which would be a query to find the target class that is nested in SomeClass and implements the Runnable interface. or targetQuery=@DexQuery(outer="SomeClass", type="java.lang.Runnable", referencedFrom="someMethod", minMatches=1, maxMatches=1)
.
the point is that query targets can generate more than one result on contentOnly=true
items and there is no reason not to allow this. if there are multiple targets, which of those should be used to rewrite the complete patch dex? (multiple method targets also have their uses, if/when contentOnly=true
is implemented for methods.)
the contract on rewrite is: only the item is rewritten, not the whole dex; no matter where the item comes from (patch or source). this is the absolute minimum change needed to keep the bytecode valid across a class rename.
in your case, you need to reference aa
directly in class2
:
@DexIgnore
public class NullInputStreamPatch {
@DexEdit(target = "aa", contentOnly = true)
public static class class1 {
@DexReplace
public InputStream b(boolean paramBoolean) {
return null;
}
}
@DexEdit(target = "bb", contentOnly = true)
public static class class2 {
@DexWrap(target = "a")
public final void function1(aa input) {
function1(input);
Log.d("debug", "on function1");
}
}
}
if you can't access the aa
name due to obfuscation issues, you can try using importSymbols=false
and define class aa
yourself with @DexIgnore
.
the issue here is that you are abusing cross-class edits to handle obfuscation. i know that obfuscation is not really supported at this time in dexpatcher so there's frustration, but this request is IMHO the wrong way around it.
a well designed and complete implementation to work with obfuscation is needed for sure, and i have it planned. it's not implemented because it depends on dex2jar functionality that was broken when dex2jar moved from the 0.99 branch to the 2.0 branch. i'm sort of maintaining dex2jar lately already (the version you are using has fixes of mine), so i don't discard the possibility of fixing this myself. but it won't happen immediately: @DexCheck
is higher priority for me, it's sort of a shame that it's not implemented yet, it's the last piece missing from a complete basic dxp-tool IMHO. a lot of important stuff could be added later to dxp-tool, but @DexCheck
is urgent.
i'm closing, but you can continue the chat here anyway.
thanks for using dxp and for taking the time to report!
Thank you