core: option for control escaping of unicode characters (#103)

This commit is contained in:
Skylot
2016-03-07 19:25:57 +03:00
parent e915f4fcd7
commit 218c39b1ec
22 changed files with 237 additions and 76 deletions
@@ -30,4 +30,6 @@ public interface IJadxArgs {
boolean isDeobfuscationForceSave();
boolean useSourceNameAsClassAlias();
boolean escapeUnicode();
}
@@ -24,6 +24,8 @@ public class JadxArgs implements IJadxArgs {
private int deobfuscationMinLength = 0;
private int deobfuscationMaxLength = Integer.MAX_VALUE;
private boolean escapeUnicode = false;
@Override
public File getOutDir() {
return outDir;
@@ -149,4 +151,13 @@ public class JadxArgs implements IJadxArgs {
public void setDeobfuscationMaxLength(int deobfuscationMaxLength) {
this.deobfuscationMaxLength = deobfuscationMaxLength;
}
@Override
public boolean escapeUnicode() {
return escapeUnicode;
}
public void setEscapeUnicode(boolean escapeUnicode) {
this.escapeUnicode = escapeUnicode;
}
}
@@ -130,11 +130,11 @@ public class AnnotationGen {
return;
}
if (val instanceof String) {
code.add(StringUtils.unescapeString((String) val));
code.add(getStringUtils().unescapeString((String) val));
} else if (val instanceof Integer) {
code.add(TypeGen.formatInteger((Integer) val));
} else if (val instanceof Character) {
code.add(StringUtils.unescapeChar((Character) val));
code.add(getStringUtils().unescapeChar((Character) val));
} else if (val instanceof Boolean) {
code.add(Boolean.TRUE.equals(val) ? "true" : "false");
} else if (val instanceof Float) {
@@ -172,4 +172,8 @@ public class AnnotationGen {
throw new JadxRuntimeException("Can't decode value: " + val + " (" + val.getClass() + ")");
}
}
private StringUtils getStringUtils() {
return cls.dex().root().getStringUtils();
}
}
@@ -348,7 +348,7 @@ public class ClassGen {
if (fv != null) {
code.add(" = ");
if (fv.getValue() == null) {
code.add(TypeGen.literalToString(0, f.getType()));
code.add(TypeGen.literalToString(0, f.getType(), cls));
} else {
if (fv.getValueType() == InitType.CONST) {
annotationGen.encodeValue(code, fv.getValue());
@@ -38,7 +38,6 @@ import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException;
@@ -130,8 +129,8 @@ public class InsnGen {
code.add(mgen.getNameGen().assignArg(arg));
}
private static String lit(LiteralArg arg) {
return TypeGen.literalToString(arg.getLiteral(), arg.getType());
private String lit(LiteralArg arg) {
return TypeGen.literalToString(arg.getLiteral(), arg.getType(), mth);
}
private void instanceField(CodeWriter code, FieldInfo field, InsnArg arg) throws CodegenException {
@@ -236,7 +235,7 @@ public class InsnGen {
switch (insn.getType()) {
case CONST_STR:
String str = ((ConstStringNode) insn).getString();
code.add(StringUtils.unescapeString(str));
code.add(mth.dex().root().getStringUtils().unescapeString(str));
break;
case CONST_CLASS:
@@ -255,7 +255,7 @@ public class RegionGen extends InsnGen {
}
}
} else if (k instanceof Integer) {
code.add(TypeGen.literalToString((Integer) k, arg.getType()));
code.add(TypeGen.literalToString((Integer) k, arg.getType(), mth));
} else {
throw new JadxRuntimeException("Unexpected key in switch: " + (k != null ? k.getClass() : null));
}
@@ -1,7 +1,9 @@
package jadx.core.codegen;
import jadx.api.JadxArgs;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.IDexNode;
import jadx.core.utils.StringUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
@@ -31,7 +33,16 @@ public class TypeGen {
*
* @throws JadxRuntimeException for incorrect type or literal value
*/
public static String literalToString(long lit, ArgType type, IDexNode dexNode) {
return literalToString(lit, type, dexNode.root().getStringUtils());
}
@Deprecated
public static String literalToString(long lit, ArgType type) {
return literalToString(lit, type, new StringUtils(new JadxArgs()));
}
private static String literalToString(long lit, ArgType type, StringUtils stringUtils) {
if (type == null || !type.isTypeKnown()) {
String n = Long.toString(lit);
if (Math.abs(lit) > 100) {
@@ -46,7 +57,7 @@ public class TypeGen {
case BOOLEAN:
return lit == 0 ? "false" : "true";
case CHAR:
return StringUtils.unescapeChar((char) lit);
return stringUtils.unescapeChar((char) lit);
case BYTE:
return formatByte((byte) lit);
case SHORT:
@@ -43,7 +43,7 @@ import com.android.dex.ClassData.Method;
import com.android.dex.ClassDef;
import com.android.dx.rop.code.AccessFlags;
public class ClassNode extends LineAttrNode implements ILoadable {
public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
private final DexNode dex;
@@ -472,10 +472,16 @@ public class ClassNode extends LineAttrNode implements ILoadable {
return accessFlags;
}
@Override
public DexNode dex() {
return dex;
}
@Override
public RootNode root() {
return dex.root();
}
public String getRawName() {
return clsInfo.getRawName();
}
@@ -28,7 +28,7 @@ import com.android.dex.MethodId;
import com.android.dex.ProtoId;
import com.android.dex.TypeList;
public class DexNode {
public class DexNode implements IDexNode {
public static final int NO_INDEX = -1;
@@ -210,10 +210,16 @@ public class DexNode {
return dexBuf.open(offset);
}
@Override
public RootNode root() {
return root;
}
@Override
public DexNode dex() {
return this;
}
@Override
public String toString() {
return "DEX";
@@ -0,0 +1,9 @@
package jadx.core.dex.nodes;
public interface IDexNode {
DexNode dex();
RootNode root();
}
@@ -44,7 +44,7 @@ import com.android.dex.Code;
import com.android.dex.Code.CatchHandler;
import com.android.dex.Code.Try;
public class MethodNode extends LineAttrNode implements ILoadable {
public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
private static final Logger LOG = LoggerFactory.getLogger(MethodNode.class);
private final MethodInfo mthInfo;
@@ -594,10 +594,16 @@ public class MethodNode extends LineAttrNode implements ILoadable {
this.region = region;
}
@Override
public DexNode dex() {
return parentClass.dex();
}
@Override
public RootNode root() {
return dex().root();
}
public MethodInfo getMethodInfo() {
return mthInfo;
}
@@ -7,6 +7,7 @@ import jadx.api.ResourcesLoader;
import jadx.core.clsp.ClspGraph;
import jadx.core.dex.info.ClassInfo;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.DexFile;
@@ -31,6 +32,7 @@ public class RootNode {
private final ErrorsCounter errorsCounter = new ErrorsCounter();
private final IJadxArgs args;
private final StringUtils stringUtils;
private List<DexNode> dexNodes;
private Map<Integer, String> resourcesNames = new HashMap<Integer, String>();
@@ -41,6 +43,7 @@ public class RootNode {
public RootNode(IJadxArgs args) {
this.args = args;
this.stringUtils = new StringUtils(args);
}
public void load(List<InputFile> inputFiles) throws DecodeException {
@@ -194,4 +197,8 @@ public class RootNode {
public IJadxArgs getArgs() {
return args;
}
public StringUtils getStringUtils() {
return stringUtils;
}
}
@@ -1,14 +1,21 @@
package jadx.core.utils;
import jadx.api.IJadxArgs;
public class StringUtils {
private StringUtils() {
private final boolean escapeUnicode;
public StringUtils(IJadxArgs args) {
this.escapeUnicode = args.escapeUnicode();
}
public static String unescapeString(String str) {
public String unescapeString(String str) {
int len = str.length();
if (len == 0) {
return "\"\"";
}
StringBuilder res = new StringBuilder();
for (int i = 0; i < len; i++) {
int c = str.charAt(i) & 0xFFFF;
processChar(c, res);
@@ -16,7 +23,7 @@ public class StringUtils {
return '"' + res.toString() + '"';
}
public static String unescapeChar(char ch) {
public String unescapeChar(char ch) {
if (ch == '\'') {
return "'\\\''";
}
@@ -25,7 +32,7 @@ public class StringUtils {
return '\'' + res.toString() + '\'';
}
private static void processChar(int c, StringBuilder res) {
private void processChar(int c, StringBuilder res) {
switch (c) {
case '\n': res.append("\\n"); break;
case '\r': res.append("\\r"); break;
@@ -37,10 +44,10 @@ public class StringUtils {
case '\\': res.append("\\\\"); break;
default:
if (32 <= c && c <= 126) {
res.append((char) c);
} else {
if (c < 32 || c >= 127 && escapeUnicode) {
res.append("\\u").append(String.format("%04x", c));
} else {
res.append((char) c);
}
break;
}
@@ -1,13 +1,17 @@
package jadx.tests
import jadx.api.JadxArgs
import jadx.core.utils.StringUtils
import spock.lang.Specification
class TestStringUtils extends Specification {
def "unescape string"() {
def args = new JadxArgs()
args.setEscapeUnicode(true)
def stringUtils = new StringUtils(args)
expect:
StringUtils.unescapeString(input) == "\"$expected\""
stringUtils.unescapeString(input) == "\"$expected\""
where:
input | expected
@@ -26,12 +30,14 @@ class TestStringUtils extends Specification {
def "unescape char"() {
expect:
StringUtils.unescapeChar(input as char) == "'$expected'"
new StringUtils(new JadxArgs()).unescapeChar(input as char) == "'$expected'"
where:
input | expected
'a' | "a"
' ' | " "
'\n' | "\\n"
'\'' | "\\\'"
'\0' | "\\u0000"
}
}