Skip to content

Commit

Permalink
fix: check full signature for search method override (#1743)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Dec 9, 2022
1 parent 12ef29b commit e1b7d36
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ private MethodOverrideAttr processOverrideMethods(MethodNode mth, SuperTypesData
for (ArgType superType : superData.getSuperTypes()) {
ClassNode classNode = mth.root().resolveClass(superType);
if (classNode != null) {
MethodNode ovrdMth = searchOverriddenMethod(classNode, signature);
MethodNode ovrdMth = searchOverriddenMethod(classNode, mth, signature);
if (ovrdMth != null) {
if (isMethodVisibleInCls(ovrdMth, cls)) {
overrideList.add(ovrdMth);
Expand All @@ -107,6 +107,8 @@ private MethodOverrideAttr processOverrideMethods(MethodNode mth, SuperTypesData
Map<String, ClspMethod> methodsMap = clsDetails.getMethodsMap();
for (Map.Entry<String, ClspMethod> entry : methodsMap.entrySet()) {
String mthShortId = entry.getKey();
// do not check full signature, classpath methods can be trusted
// i.e. doesn't contain methods with same signature in one class
if (mthShortId.startsWith(signature)) {
overrideList.add(entry.getValue());
break;
Expand All @@ -130,12 +132,30 @@ private void addBaseMethod(SuperTypesData superData, List<IMethodDetails> overri
}

@Nullable
private MethodNode searchOverriddenMethod(ClassNode cls, String signature) {
private MethodNode searchOverriddenMethod(ClassNode cls, MethodNode mth, String signature) {
// search by exact full signature (with return value) to fight obfuscation (see test
// 'TestOverrideWithSameName')
String shortId = mth.getMethodInfo().getShortId();
for (MethodNode supMth : cls.getMethods()) {
if (!supMth.getAccessFlags().isStatic() && supMth.getMethodInfo().getShortId().startsWith(signature)) {
if (supMth.getMethodInfo().getShortId().equals(shortId) && !supMth.getAccessFlags().isStatic()) {
return supMth;
}
}
// search by signature without return value and check if return value is wider type
for (MethodNode supMth : cls.getMethods()) {
if (supMth.getMethodInfo().getShortId().startsWith(signature) && !supMth.getAccessFlags().isStatic()) {
TypeCompare typeCompare = cls.root().getTypeCompare();
ArgType supRetType = supMth.getMethodInfo().getReturnType();
ArgType mthRetType = mth.getMethodInfo().getReturnType();
TypeCompareEnum res = typeCompare.compareTypes(supRetType, mthRetType);
if (res.isWider()) {
return supMth;
}
if (res == TypeCompareEnum.UNKNOWN || res == TypeCompareEnum.CONFLICT) {
mth.addDebugComment("Possible override for method " + supMth.getMethodInfo().getFullId());
}
}
}
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package jadx.tests.integration.others;

import java.util.List;

import org.junit.jupiter.api.Test;

import jadx.core.dex.attributes.AType;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;

import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;

@SuppressWarnings("CommentedOutCode")
public class TestOverrideWithSameName extends SmaliTest {

//@formatter:off
/*
interface A {
B a();
C a();
}
abstract class B implements A {
@Override
public C a() {
return null;
}
}
public class C extends B {
@Override
public B a() {
return null;
}
}
*/
//@formatter:on

@Test
public void test() {
List<ClassNode> clsNodes = loadFromSmaliFiles();
assertThat(searchCls(clsNodes, "test.A"))
.code()
.containsOne("C mo0a();") // assume second method was renamed
.doesNotContain("@Override");

ClassNode bCls = searchCls(clsNodes, "test.B");
assertThat(bCls)
.code()
.containsOne("C mo0a() {")
.containsOne("@Override");

assertThat(getMethod(bCls, "a").get(AType.METHOD_OVERRIDE).getOverrideList())
.singleElement()
.satisfies(mth -> assertThat(mth.getMethodInfo().getDeclClass().getShortName()).isEqualTo("A"));

ClassNode cCls = searchCls(clsNodes, "test.C");
assertThat(cCls)
.code()
.containsOne("B a() {")
.containsOne("@Override");

assertThat(getMethod(cCls, "a").get(AType.METHOD_OVERRIDE).getOverrideList())
.singleElement()
.satisfies(mth -> assertThat(mth.getMethodInfo().getDeclClass().getShortName()).isEqualTo("A"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.class interface abstract Ltest/A;
.super Ljava/lang/Object;

.method public abstract a()Ltest/B;
.end method

.method public abstract a()Ltest/C;
.end method
10 changes: 10 additions & 0 deletions jadx-core/src/test/smali/others/TestOverrideWithSameName/B.smali
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.class abstract Ltest/B;
.super Ljava/lang/Object;

.implements Ltest/A;

.method public a()Ltest/C;
.registers 2
const/4 v0, 0x0
return-object v0
.end method
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.class public Ltest/C;
.super Ltest/B;

.method public a()Ltest/B;
.registers 2
const/4 v0, 0x0
return-object v0
.end method

0 comments on commit e1b7d36

Please sign in to comment.