提交 91ee7565 编写于 作者: S Skylot

fix: resolved regression in Kotlin metadata parser

上级 1bbcac2a
......@@ -28,6 +28,7 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.kotlin.KotlinMetadataUtils;
public class Deobfuscator {
private static final Logger LOG = LoggerFactory.getLogger(Deobfuscator.class);
......@@ -36,9 +37,6 @@ public class Deobfuscator {
public static final String CLASS_NAME_SEPARATOR = ".";
public static final String INNER_CLASS_SEPARATOR = "$";
public static final String KOTLIN_METADATA_ANNOTATION = "kotlin.Metadata";
public static final String KOTLIN_METADATA_D2_PARAMETER = "d2";
public static final String KOTLIN_METADATA_CLASSNAME_REGEX = "(L.*;)";
private final JadxArgs args;
private final RootNode root;
......@@ -351,9 +349,8 @@ public class Deobfuscator {
} else {
if (!clsMap.containsKey(classInfo)) {
String clsShortName = classInfo.getShortName();
if (shouldRename(clsShortName) || reservedClsNames.contains(clsShortName)) {
makeClsAlias(cls);
}
boolean badName = shouldRename(clsShortName) || reservedClsNames.contains(clsShortName);
makeClsAlias(cls, badName);
}
}
for (ClassNode innerCls : cls.getInnerClasses()) {
......@@ -366,7 +363,7 @@ public class Deobfuscator {
if (deobfClsInfo != null) {
return deobfClsInfo.getAlias();
}
return makeClsAlias(cls);
return makeClsAlias(cls, true);
}
public String getPkgAlias(ClassNode cls) {
......@@ -387,41 +384,35 @@ public class Deobfuscator {
}
}
private String makeClsAlias(ClassNode cls) {
ClassInfo classInfo = cls.getClassInfo();
String metadataClassName = "";
String metadataPackageName = "";
private String makeClsAlias(ClassNode cls, boolean badName) {
String alias = null;
String pkgName = null;
if (this.parseKotlinMetadata) {
String rawClassName = getRawClassNameFromMetadata(cls);
if (rawClassName != null) {
metadataClassName = rawClassName.substring(rawClassName.lastIndexOf(".") + 1, rawClassName.length() - 1);
if (rawClassName.lastIndexOf(".") != -1) {
metadataPackageName = rawClassName.substring(1, rawClassName.lastIndexOf("."));
ClassInfo kotlinCls = KotlinMetadataUtils.getClassName(cls);
if (kotlinCls != null) {
alias = prepareNameFull(kotlinCls.getShortName(), "C");
pkgName = kotlinCls.getPackage();
}
}
}
String alias = null;
if (this.useSourceNameAsAlias) {
if (alias == null && this.useSourceNameAsAlias) {
alias = getAliasFromSourceFile(cls);
}
ClassInfo classInfo = cls.getClassInfo();
if (alias == null) {
if (metadataClassName.isEmpty()) {
if (badName) {
String clsName = classInfo.getShortName();
String prefix = makeClsPrefix(cls);
alias = String.format("%sC%04d%s", prefix, clsIndex++, prepareNamePart(clsName));
} else {
alias = metadataClassName;
// rename not needed
return classInfo.getShortName();
}
}
PackageNode pkg;
if (metadataPackageName.isEmpty()) {
pkg = getPackageNode(classInfo.getPackage(), true);
} else {
pkg = getPackageNode(metadataPackageName, true);
if (pkgName == null) {
pkgName = classInfo.getPackage();
}
PackageNode pkg = getPackageNode(pkgName, true);
clsMap.put(classInfo, new DeobfClsInfo(this, cls, pkg, alias));
return alias;
}
......@@ -484,29 +475,6 @@ public class Deobfuscator {
return result;
}
/**
* Try to get class name form Kotlin meta data
*
* @param cls
* @return
*/
@Nullable
private String getRawClassNameFromMetadata(ClassNode cls) {
if (cls.getAnnotation(KOTLIN_METADATA_ANNOTATION) != null
&& cls.getAnnotation(KOTLIN_METADATA_ANNOTATION).getValues().get(KOTLIN_METADATA_D2_PARAMETER) != null
&& cls.getAnnotation(KOTLIN_METADATA_ANNOTATION).getValues().get(KOTLIN_METADATA_D2_PARAMETER) instanceof List) {
Object rawClassNameObject =
((List) cls.getAnnotation(KOTLIN_METADATA_ANNOTATION).getValues().get(KOTLIN_METADATA_D2_PARAMETER)).get(0);
if (rawClassNameObject instanceof String) {
String rawClassName = ((String) rawClassNameObject).trim().replace("/", ".");
if (rawClassName.length() > 1 && rawClassName.matches(KOTLIN_METADATA_CLASSNAME_REGEX)) {
return rawClassName;
}
}
}
return null;
}
@Nullable
private String getAliasFromSourceFile(ClassNode cls) {
SourceFileAttr sourceFileAttr = cls.get(AType.SOURCE_FILE);
......@@ -628,6 +596,24 @@ public class Deobfuscator {
return NameMapper.removeInvalidCharsMiddle(name);
}
private String prepareNameFull(String name, String prefix) {
if (name.length() > maxLength) {
return makeHashName(name, prefix);
}
String result = NameMapper.removeInvalidChars(name, prefix);
if (result.isEmpty()) {
return makeHashName(name, prefix);
}
if (NameMapper.isReserved(result)) {
return prefix + result;
}
return result;
}
private static String makeHashName(String name, String invalidPrefix) {
return invalidPrefix + 'x' + Integer.toHexString(name.hashCode());
}
private void dumpClassAlias(ClassNode cls) {
PackageNode pkg = getPackageNode(cls.getPackage(), false);
......
package jadx.core.utils.kotlin;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.plugins.input.data.annotations.EncodedType;
import jadx.api.plugins.input.data.annotations.EncodedValue;
import jadx.api.plugins.input.data.annotations.IAnnotation;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.nodes.ClassNode;
// TODO: parse data from d1 (protobuf encoded) to get original method names and other useful info
public class KotlinMetadataUtils {
private static final Logger LOG = LoggerFactory.getLogger(KotlinMetadataUtils.class);
private static final String KOTLIN_METADATA_ANNOTATION = "Lkotlin/Metadata;";
private static final String KOTLIN_METADATA_D2_PARAMETER = "d2";
private static final String KOTLIN_METADATA_CLASSNAME_REGEX = "(L.*;)";
/**
* Try to get class info from Kotlin Metadata annotation
*/
@Nullable
public static ClassInfo getClassName(ClassNode cls) {
IAnnotation metadataAnnotation = cls.getAnnotation(KOTLIN_METADATA_ANNOTATION);
List<EncodedValue> d2Param = getParamAsList(metadataAnnotation, KOTLIN_METADATA_D2_PARAMETER);
if (d2Param == null || d2Param.isEmpty()) {
return null;
}
EncodedValue firstValue = d2Param.get(0);
if (firstValue == null || firstValue.getType() != EncodedType.ENCODED_STRING) {
return null;
}
try {
String rawClassName = ((String) firstValue.getValue()).trim();
if (rawClassName.matches(KOTLIN_METADATA_CLASSNAME_REGEX)) {
return ClassInfo.fromName(cls.root(), rawClassName);
}
} catch (Exception e) {
LOG.error("Failed to parse kotlin metadata", e);
}
return null;
}
@SuppressWarnings("unchecked")
private static List<EncodedValue> getParamAsList(IAnnotation annotation, String paramName) {
if (annotation == null) {
return null;
}
EncodedValue encodedValue = annotation.getValues().get(paramName);
if (encodedValue == null || encodedValue.getType() != EncodedType.ENCODED_ARRAY) {
return null;
}
return (List<EncodedValue>) encodedValue.getValue();
}
}
package jadx.tests.integration.deobf;
import org.junit.jupiter.api.Test;
import jadx.tests.api.SmaliTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestKotlinMetadata extends SmaliTest {
// @formatter:off
/*
@file:JvmName("TestKotlinMetadata")
class TestMetaData {
@JvmField
val id = 1
@JvmName("makeTwo")
fun double(x: Int): Int {
return 2 * x
}
}
*/
// @formatter:on
@Test
public void test() {
prepareArgs(true);
assertThat(getClassNodeFromSmali())
.code()
.containsOne("class TestMetaData {");
}
@Test
public void testIgnoreMetadata() {
prepareArgs(false);
assertThat(getClassNodeFromSmali())
.code()
.containsOne("class C0000TestKotlinMetadata {");
}
private void prepareArgs(boolean parseKotlinMetadata) {
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
getArgs().setParseKotlinMetadata(parseKotlinMetadata);
disableCompilation();
}
}
.class public final Ldeobf/TestKotlinMetadata;
.super Ljava/lang/Object;
.source "TestMetaData.kt"
# annotations
.annotation runtime Lkotlin/Metadata;
bv = {
0x1,
0x0,
0x3
}
d1 = {
"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\u0008\u0002\n\u0002\u0010\u0008\n\u0002\u0008\u0004\u0018\u00002\u00020\u0001B\u0005\u00a2\u0006\u0002\u0010\u0002J\u0015\u0010\u0005\u001a\u00020\u00042\u0006\u0010\u0006\u001a\u00020\u0004H\u0007\u00a2\u0006\u0002\u0008\u0007R\u0010\u0010\u0003\u001a\u00020\u00048\u0006X\u0087D\u00a2\u0006\u0002\n\u0000\u00a8\u0006\u0008"
}
d2 = {
"LTestMetaData;",
"",
"()V",
"id",
"",
"double",
"x",
"makeTwo",
"test"
}
k = 0x1
mv = {
0x1,
0x4,
0x0
}
.end annotation
# instance fields
.field public final id:I
.annotation build Lkotlin/jvm/JvmField;
.end annotation
.end field
# direct methods
.method public constructor <init>()V
.registers 2
.prologue
.line 4
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
.line 7
const/4 v0, 0x1
iput v0, p0, Ldeobf/TestKotlinMetadata;->id:I
return-void
.end method
# virtual methods
.method public final makeTwo(I)I
.registers 3
.param p1, "x" # I
.annotation build Lkotlin/jvm/JvmName;
name = "makeTwo"
.end annotation
.prologue
.line 11
mul-int/lit8 v0, p1, 0x2
return v0
.end method
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册