Commit 9856b6d3 authored by Skylot's avatar Skylot

fix: remove invalid chars from class names (#453)

parent e1ca2904
......@@ -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()) {
......
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"));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment