fix: remove invalid chars from class names (#453)
This commit is contained in:
@@ -483,21 +483,7 @@ public class Deobfuscator {
|
||||
if (name.length() > maxLength) {
|
||||
return "x" + Integer.toHexString(name.hashCode());
|
||||
}
|
||||
if (!NameMapper.isAllCharsPrintable(name)) {
|
||||
return removeInvalidChars(name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private String removeInvalidChars(String name) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
int ch = name.charAt(i);
|
||||
if (NameMapper.isPrintableChar(ch)) {
|
||||
sb.append((char) ch);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
return NameMapper.removeInvalidCharsMiddle(name);
|
||||
}
|
||||
|
||||
private void dumpClassAlias(ClassNode cls) {
|
||||
|
||||
@@ -91,6 +91,14 @@ public class NameMapper {
|
||||
&& isAllCharsPrintable(str);
|
||||
}
|
||||
|
||||
public static boolean isValidIdentifierStart(int codePoint) {
|
||||
return Character.isJavaIdentifierStart(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isValidIdentifierPart(int codePoint) {
|
||||
return Character.isJavaIdentifierPart(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isPrintableChar(int c) {
|
||||
return 32 <= c && c <= 126;
|
||||
}
|
||||
@@ -105,6 +113,49 @@ public class NameMapper {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return modified string with removed:
|
||||
* <p><ul>
|
||||
* <li> not printable chars (including unicode)
|
||||
* <li> chars not valid for java identifier part
|
||||
* </ul><p>
|
||||
* Note: this 'middle' method must be used with prefixed string:
|
||||
* <p><ul>
|
||||
* <li> can leave invalid chars for java identifier start (i.e numbers)
|
||||
* <li> result not checked for reserved words
|
||||
* </ul><p>
|
||||
*/
|
||||
public static String removeInvalidCharsMiddle(String name) {
|
||||
if (isValidIdentifier(name) && isAllCharsPrintable(name)) {
|
||||
return name;
|
||||
}
|
||||
int len = name.length();
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
int codePoint = name.codePointAt(i);
|
||||
if (isPrintableChar(codePoint) && isValidIdentifierPart(codePoint)) {
|
||||
sb.append((char) codePoint);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string with removed invalid chars, see {@link #removeInvalidCharsMiddle}
|
||||
* <p>
|
||||
* Prepend prefix if first char is not valid as java identifier start char.
|
||||
*/
|
||||
public static String removeInvalidChars(String name, String prefix) {
|
||||
String result = removeInvalidCharsMiddle(name);
|
||||
if (!result.isEmpty()) {
|
||||
int codePoint = result.codePointAt(0);
|
||||
if (!isValidIdentifierStart(codePoint)) {
|
||||
return prefix + result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private NameMapper() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,14 +72,9 @@ public class RenameVisitor extends AbstractVisitor {
|
||||
ClassInfo classInfo = cls.getClassInfo();
|
||||
ClassInfo alias = classInfo.getAlias();
|
||||
String clsName = alias.getShortName();
|
||||
String newShortName = null;
|
||||
char firstChar = clsName.charAt(0);
|
||||
if (Character.isDigit(firstChar)) {
|
||||
newShortName = Consts.ANONYMOUS_CLASS_PREFIX + clsName;
|
||||
} else if (firstChar == '$') {
|
||||
newShortName = "C" + clsName;
|
||||
}
|
||||
if (newShortName != null) {
|
||||
|
||||
String newShortName = fixClsShortName(clsName);
|
||||
if (!newShortName.equals(clsName)) {
|
||||
classInfo.rename(cls.root(), alias.makeFullClsName(newShortName, true));
|
||||
}
|
||||
if (alias.getPackage().isEmpty()) {
|
||||
@@ -89,6 +84,17 @@ public class RenameVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private String fixClsShortName(String clsName) {
|
||||
char firstChar = clsName.charAt(0);
|
||||
if (Character.isDigit(firstChar)) {
|
||||
return Consts.ANONYMOUS_CLASS_PREFIX + NameMapper.removeInvalidCharsMiddle(clsName);
|
||||
}
|
||||
if (firstChar == '$') {
|
||||
return 'C' + NameMapper.removeInvalidCharsMiddle(clsName);
|
||||
}
|
||||
return NameMapper.removeInvalidChars(clsName, "C");
|
||||
}
|
||||
|
||||
private void checkFields(ClassNode cls) {
|
||||
Set<String> names = new HashSet<>();
|
||||
for (FieldNode field : cls.getFields()) {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.core.deobf.NameMapper.isValidIdentifier;
|
||||
import static jadx.core.deobf.NameMapper.removeInvalidChars;
|
||||
import static jadx.core.deobf.NameMapper.removeInvalidCharsMiddle;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class NameMapperTest {
|
||||
|
||||
@Test
|
||||
public void validIdentifiers() {
|
||||
assertThat(isValidIdentifier("ACls"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notValidIdentifiers() {
|
||||
assertThat(isValidIdentifier("1cls"), is(false));
|
||||
assertThat(isValidIdentifier("-cls"), is(false));
|
||||
assertThat(isValidIdentifier("A-cls"), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveInvalidCharsMiddle() {
|
||||
assertThat(removeInvalidCharsMiddle("1cls"), is("1cls"));
|
||||
assertThat(removeInvalidCharsMiddle("-cls"), is("cls"));
|
||||
assertThat(removeInvalidCharsMiddle("A-cls"), is("Acls"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveInvalidChars() {
|
||||
assertThat(removeInvalidChars("1cls", "C"), is("C1cls"));
|
||||
assertThat(removeInvalidChars("-cls", "C"), is("cls"));
|
||||
assertThat(removeInvalidChars("A-cls", "C"), is("Acls"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user