未验证 提交 358cddd9 编写于 作者: S Skylot

fix: support dynamic strings concat (#1250)

上级 418df2fd
......@@ -26,6 +26,8 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.input.InsnDataUtils;
public class InsnDecoder {
private static final Logger LOG = LoggerFactory.getLogger(InsnDecoder.class);
......@@ -567,12 +569,9 @@ public class InsnDecoder {
if (type == InvokeType.CUSTOM) {
return InvokeCustomBuilder.build(method, insn, isRange);
}
IMethodRef mthRef;
ICustomPayload payload = insn.getPayload();
if (payload != null) {
mthRef = ((IMethodRef) payload);
} else {
mthRef = insn.getIndexAsMethod();
IMethodRef mthRef = InsnDataUtils.getMethodRef(insn);
if (mthRef == null) {
throw new JadxRuntimeException("Failed to load method reference for insn: " + insn);
}
MethodInfo mthInfo = MethodInfo.fromRef(root, mthRef);
return new InvokeNode(mthInfo, insn, type, isRange);
......
......@@ -2,184 +2,36 @@ package jadx.core.dex.instructions;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import jadx.api.plugins.input.data.ICallSite;
import jadx.api.plugins.input.data.IMethodHandle;
import jadx.api.plugins.input.data.IMethodProto;
import jadx.api.plugins.input.data.IMethodRef;
import jadx.api.plugins.input.data.MethodHandleType;
import jadx.api.plugins.input.data.annotations.EncodedValue;
import jadx.api.plugins.input.insns.InsnData;
import jadx.api.plugins.input.insns.custom.ICustomPayload;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.NamedArg;
import jadx.core.dex.instructions.mods.ConstructorInsn;
import jadx.core.dex.instructions.invokedynamic.CustomLambdaCall;
import jadx.core.dex.instructions.invokedynamic.CustomStringConcat;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.input.InsnDataUtils;
public class InvokeCustomBuilder {
public static InsnNode build(MethodNode mth, InsnData insn, boolean isRange) {
try {
ICallSite callSite;
ICustomPayload payload = insn.getPayload();
if (payload != null) {
callSite = (ICallSite) payload;
} else {
callSite = insn.getIndexAsCallSite();
ICallSite callSite = InsnDataUtils.getCallSite(insn);
if (callSite == null) {
throw new JadxRuntimeException("Failed to get call site for insn: " + insn);
}
callSite.load();
List<EncodedValue> values = callSite.getValues();
if (!checkLinkerMethod(values)) {
throw new JadxRuntimeException("Failed to process invoke-custom instruction: " + callSite);
}
IMethodHandle callMthHandle = (IMethodHandle) values.get(4).getValue();
if (callMthHandle.getType().isField()) {
throw new JadxRuntimeException("Not yet supported");
if (CustomLambdaCall.isLambdaInvoke(values)) {
return CustomLambdaCall.buildLambdaMethodCall(mth, insn, isRange, values);
}
InvokeCustomNode resNode = buildMethodCall(mth, insn, isRange, values, callMthHandle);
int resReg = insn.getResultReg();
if (resReg != -1) {
resNode.setResult(InsnArg.reg(resReg, mth.getReturnType()));
if (CustomStringConcat.isStringConcat(values)) {
return CustomStringConcat.buildStringConcat(insn, isRange, values);
}
return resNode;
// TODO: output raw dynamic call
throw new JadxRuntimeException("Failed to process invoke-custom instruction: " + callSite);
} catch (Exception e) {
throw new JadxRuntimeException("'invoke-custom' instruction processing error: " + e.getMessage(), e);
}
}
@NotNull
private static InvokeCustomNode buildMethodCall(MethodNode mth, InsnData insn, boolean isRange,
List<EncodedValue> values, IMethodHandle callMthHandle) {
RootNode root = mth.root();
IMethodProto lambdaProto = (IMethodProto) values.get(2).getValue();
MethodInfo lambdaInfo = MethodInfo.fromMethodProto(root, mth.getParentClass().getClassInfo(), "", lambdaProto);
MethodHandleType methodHandleType = callMthHandle.getType();
InvokeCustomNode invokeCustomNode = new InvokeCustomNode(lambdaInfo, insn, false, isRange);
invokeCustomNode.setHandleType(methodHandleType);
ClassInfo implCls = ClassInfo.fromType(root, lambdaInfo.getReturnType());
String implName = (String) values.get(1).getValue();
IMethodProto implProto = (IMethodProto) values.get(3).getValue();
MethodInfo implMthInfo = MethodInfo.fromMethodProto(root, implCls, implName, implProto);
invokeCustomNode.setImplMthInfo(implMthInfo);
MethodInfo callMthInfo = MethodInfo.fromRef(root, callMthHandle.getMethodRef());
InvokeNode invokeNode = buildInvokeNode(methodHandleType, invokeCustomNode, callMthInfo);
if (methodHandleType == MethodHandleType.INVOKE_CONSTRUCTOR) {
ConstructorInsn ctrInsn = new ConstructorInsn(mth, invokeNode);
invokeCustomNode.setCallInsn(ctrInsn);
} else {
invokeCustomNode.setCallInsn(invokeNode);
}
MethodNode callMth = root.resolveMethod(callMthInfo);
if (callMth != null) {
invokeCustomNode.getCallInsn().addAttr(callMth);
if (callMth.getAccessFlags().isSynthetic()
&& callMth.getParentClass().equals(mth.getParentClass())) {
// inline only synthetic methods from same class
callMth.add(AFlag.DONT_GENERATE);
invokeCustomNode.setInlineInsn(true);
}
}
if (!invokeCustomNode.isInlineInsn()) {
IMethodProto effectiveMthProto = (IMethodProto) values.get(5).getValue();
List<ArgType> args = Utils.collectionMap(effectiveMthProto.getArgTypes(), ArgType::parse);
boolean sameArgs = args.equals(callMthInfo.getArgumentsTypes());
invokeCustomNode.setUseRef(sameArgs);
}
// prevent args inlining into not generated invoke custom node
for (InsnArg arg : invokeCustomNode.getArguments()) {
arg.add(AFlag.DONT_INLINE);
}
return invokeCustomNode;
}
@NotNull
private static InvokeNode buildInvokeNode(MethodHandleType methodHandleType, InvokeCustomNode invokeCustomNode,
MethodInfo callMthInfo) {
InvokeType invokeType = convertInvokeType(methodHandleType);
int callArgsCount = callMthInfo.getArgsCount();
boolean instanceCall = invokeType != InvokeType.STATIC;
if (instanceCall) {
callArgsCount++;
}
InvokeNode invokeNode = new InvokeNode(callMthInfo, invokeType, callArgsCount);
// copy insn args
int argsCount = invokeCustomNode.getArgsCount();
for (int i = 0; i < argsCount; i++) {
InsnArg arg = invokeCustomNode.getArg(i);
invokeNode.addArg(arg.duplicate());
}
if (callArgsCount > argsCount) {
// fill remaining args with NamedArg
int callArgNum = argsCount;
if (instanceCall) {
callArgNum--; // start from instance type
}
List<ArgType> callArgTypes = callMthInfo.getArgumentsTypes();
for (int i = argsCount; i < callArgsCount; i++) {
ArgType argType;
if (callArgNum < 0) {
// instance arg type
argType = callMthInfo.getDeclClass().getType();
} else {
argType = callArgTypes.get(callArgNum++);
}
invokeNode.addArg(new NamedArg("v" + i, argType));
}
}
return invokeNode;
}
/**
* Expect LambdaMetafactory.metafactory method
*/
private static boolean checkLinkerMethod(List<EncodedValue> values) {
if (values.size() < 6) {
return false;
}
IMethodHandle methodHandle = (IMethodHandle) values.get(0).getValue();
if (methodHandle.getType() != MethodHandleType.INVOKE_STATIC) {
return false;
}
IMethodRef methodRef = methodHandle.getMethodRef();
if (!methodRef.getName().equals("metafactory")) {
return false;
}
if (!methodRef.getParentClassType().equals("Ljava/lang/invoke/LambdaMetafactory;")) {
return false;
}
return true;
}
private static InvokeType convertInvokeType(MethodHandleType type) {
switch (type) {
case INVOKE_STATIC:
return InvokeType.STATIC;
case INVOKE_INSTANCE:
return InvokeType.VIRTUAL;
case INVOKE_DIRECT:
case INVOKE_CONSTRUCTOR:
return InvokeType.DIRECT;
case INVOKE_INTERFACE:
return InvokeType.INTERFACE;
default:
throw new JadxRuntimeException("Unsupported method handle type: " + type);
}
}
}
package jadx.core.dex.instructions.invokedynamic;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import jadx.api.plugins.input.data.IMethodHandle;
import jadx.api.plugins.input.data.IMethodProto;
import jadx.api.plugins.input.data.IMethodRef;
import jadx.api.plugins.input.data.MethodHandleType;
import jadx.api.plugins.input.data.annotations.EncodedValue;
import jadx.api.plugins.input.insns.InsnData;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.InvokeCustomNode;
import jadx.core.dex.instructions.InvokeNode;
import jadx.core.dex.instructions.InvokeType;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.NamedArg;
import jadx.core.dex.instructions.mods.ConstructorInsn;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class CustomLambdaCall {
/**
* Expect LambdaMetafactory.metafactory method
*/
public static boolean isLambdaInvoke(List<EncodedValue> values) {
if (values.size() < 6) {
return false;
}
IMethodHandle methodHandle = (IMethodHandle) values.get(0).getValue();
if (methodHandle.getType() != MethodHandleType.INVOKE_STATIC) {
return false;
}
IMethodRef methodRef = methodHandle.getMethodRef();
if (!methodRef.getName().equals("metafactory")) {
return false;
}
if (!methodRef.getParentClassType().equals("Ljava/lang/invoke/LambdaMetafactory;")) {
return false;
}
return true;
}
public static InvokeCustomNode buildLambdaMethodCall(MethodNode mth, InsnData insn, boolean isRange, List<EncodedValue> values) {
IMethodHandle callMthHandle = (IMethodHandle) values.get(4).getValue();
if (callMthHandle.getType().isField()) {
throw new JadxRuntimeException("Not yet supported");
}
InvokeCustomNode resNode = buildMethodCall(mth, insn, isRange, values, callMthHandle);
int resReg = insn.getResultReg();
if (resReg != -1) {
resNode.setResult(InsnArg.reg(resReg, mth.getReturnType()));
}
return resNode;
}
@NotNull
private static InvokeCustomNode buildMethodCall(MethodNode mth, InsnData insn, boolean isRange,
List<EncodedValue> values, IMethodHandle callMthHandle) {
RootNode root = mth.root();
IMethodProto lambdaProto = (IMethodProto) values.get(2).getValue();
MethodInfo lambdaInfo = MethodInfo.fromMethodProto(root, mth.getParentClass().getClassInfo(), "", lambdaProto);
MethodHandleType methodHandleType = callMthHandle.getType();
InvokeCustomNode invokeCustomNode = new InvokeCustomNode(lambdaInfo, insn, false, isRange);
invokeCustomNode.setHandleType(methodHandleType);
ClassInfo implCls = ClassInfo.fromType(root, lambdaInfo.getReturnType());
String implName = (String) values.get(1).getValue();
IMethodProto implProto = (IMethodProto) values.get(3).getValue();
MethodInfo implMthInfo = MethodInfo.fromMethodProto(root, implCls, implName, implProto);
invokeCustomNode.setImplMthInfo(implMthInfo);
MethodInfo callMthInfo = MethodInfo.fromRef(root, callMthHandle.getMethodRef());
InvokeNode invokeNode = buildInvokeNode(methodHandleType, invokeCustomNode, callMthInfo);
if (methodHandleType == MethodHandleType.INVOKE_CONSTRUCTOR) {
ConstructorInsn ctrInsn = new ConstructorInsn(mth, invokeNode);
invokeCustomNode.setCallInsn(ctrInsn);
} else {
invokeCustomNode.setCallInsn(invokeNode);
}
MethodNode callMth = root.resolveMethod(callMthInfo);
if (callMth != null) {
invokeCustomNode.getCallInsn().addAttr(callMth);
if (callMth.getAccessFlags().isSynthetic()
&& callMth.getParentClass().equals(mth.getParentClass())) {
// inline only synthetic methods from same class
callMth.add(AFlag.DONT_GENERATE);
invokeCustomNode.setInlineInsn(true);
}
}
if (!invokeCustomNode.isInlineInsn()) {
IMethodProto effectiveMthProto = (IMethodProto) values.get(5).getValue();
List<ArgType> args = Utils.collectionMap(effectiveMthProto.getArgTypes(), ArgType::parse);
boolean sameArgs = args.equals(callMthInfo.getArgumentsTypes());
invokeCustomNode.setUseRef(sameArgs);
}
// prevent args inlining into not generated invoke custom node
for (InsnArg arg : invokeCustomNode.getArguments()) {
arg.add(AFlag.DONT_INLINE);
}
return invokeCustomNode;
}
@NotNull
private static InvokeNode buildInvokeNode(MethodHandleType methodHandleType, InvokeCustomNode invokeCustomNode,
MethodInfo callMthInfo) {
InvokeType invokeType = convertInvokeType(methodHandleType);
int callArgsCount = callMthInfo.getArgsCount();
boolean instanceCall = invokeType != InvokeType.STATIC;
if (instanceCall) {
callArgsCount++;
}
InvokeNode invokeNode = new InvokeNode(callMthInfo, invokeType, callArgsCount);
// copy insn args
int argsCount = invokeCustomNode.getArgsCount();
for (int i = 0; i < argsCount; i++) {
InsnArg arg = invokeCustomNode.getArg(i);
invokeNode.addArg(arg.duplicate());
}
if (callArgsCount > argsCount) {
// fill remaining args with NamedArg
int callArgNum = argsCount;
if (instanceCall) {
callArgNum--; // start from instance type
}
List<ArgType> callArgTypes = callMthInfo.getArgumentsTypes();
for (int i = argsCount; i < callArgsCount; i++) {
ArgType argType;
if (callArgNum < 0) {
// instance arg type
argType = callMthInfo.getDeclClass().getType();
} else {
argType = callArgTypes.get(callArgNum++);
}
invokeNode.addArg(new NamedArg("v" + i, argType));
}
}
return invokeNode;
}
private static InvokeType convertInvokeType(MethodHandleType type) {
switch (type) {
case INVOKE_STATIC:
return InvokeType.STATIC;
case INVOKE_INSTANCE:
return InvokeType.VIRTUAL;
case INVOKE_DIRECT:
case INVOKE_CONSTRUCTOR:
return InvokeType.DIRECT;
case INVOKE_INTERFACE:
return InvokeType.INTERFACE;
default:
throw new JadxRuntimeException("Unsupported method handle type: " + type);
}
}
}
package jadx.core.dex.instructions.invokedynamic;
import java.util.List;
import java.util.Objects;
import jadx.api.plugins.input.data.IMethodHandle;
import jadx.api.plugins.input.data.IMethodRef;
import jadx.api.plugins.input.data.MethodHandleType;
import jadx.api.plugins.input.data.annotations.EncodedType;
import jadx.api.plugins.input.data.annotations.EncodedValue;
import jadx.api.plugins.input.insns.InsnData;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.JadxError;
import jadx.core.dex.instructions.ConstClassNode;
import jadx.core.dex.instructions.ConstStringNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.utils.EncodedValueUtils;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class CustomStringConcat {
public static boolean isStringConcat(List<EncodedValue> values) {
if (values.size() < 4) {
return false;
}
IMethodHandle methodHandle = (IMethodHandle) values.get(0).getValue();
if (methodHandle.getType() != MethodHandleType.INVOKE_STATIC) {
return false;
}
IMethodRef methodRef = methodHandle.getMethodRef();
if (!methodRef.getName().equals("makeConcatWithConstants")) {
return false;
}
if (!methodRef.getParentClassType().equals("Ljava/lang/invoke/StringConcatFactory;")) {
return false;
}
if (!Objects.equals(values.get(1).getValue(), "makeConcatWithConstants")) {
return false;
}
if (values.get(3).getType() != EncodedType.ENCODED_STRING) {
return false;
}
return true;
}
public static InsnNode buildStringConcat(InsnData insn, boolean isRange, List<EncodedValue> values) {
try {
int argsCount = values.size() - 3 + insn.getRegsCount();
InsnNode concat = new InsnNode(InsnType.STR_CONCAT, argsCount);
String recipe = (String) values.get(3).getValue();
processRecipe(recipe, concat, values, insn);
int resReg = insn.getResultReg();
if (resReg != -1) {
concat.setResult(InsnArg.reg(resReg, ArgType.STRING));
}
return concat;
} catch (Exception e) {
InsnNode nop = new InsnNode(InsnType.NOP, 0);
nop.add(AFlag.SYNTHETIC);
nop.addAttr(AType.JADX_ERROR, new JadxError("Failed to process dynamic string concat: " + e.getMessage(), e));
return nop;
}
}
private static void processRecipe(String recipe, InsnNode concat, List<EncodedValue> values, InsnData insn) {
int len = recipe.length();
int offset = 0;
int argNum = 0;
int constNum = 4;
StringBuilder sb = new StringBuilder(len);
while (offset < len) {
int cp = recipe.codePointAt(offset);
offset += Character.charCount(cp);
boolean argTag = cp == 1;
boolean constTag = cp == 2;
if (argTag || constTag) {
if (sb.length() != 0) {
concat.addArg(InsnArg.wrapArg(new ConstStringNode(sb.toString())));
sb.setLength(0);
}
if (argTag) {
concat.addArg(InsnArg.reg(insn, argNum++, ArgType.UNKNOWN));
} else {
InsnArg constArg = buildInsnArgFromEncodedValue(values.get(constNum++));
concat.addArg(constArg);
}
} else {
sb.appendCodePoint(cp);
}
}
if (sb.length() != 0) {
concat.addArg(InsnArg.wrapArg(new ConstStringNode(sb.toString())));
}
}
private static InsnArg buildInsnArgFromEncodedValue(EncodedValue encodedValue) {
Object value = EncodedValueUtils.convertToConstValue(encodedValue);
if (value == null) {
return InsnArg.lit(0, ArgType.UNKNOWN);
}
if (value instanceof LiteralArg) {
return ((LiteralArg) value);
}
if (value instanceof ArgType) {
return InsnArg.wrapArg(new ConstClassNode((ArgType) value));
}
if (value instanceof String) {
return InsnArg.wrapArg(new ConstStringNode(((String) value)));
}
throw new JadxRuntimeException("Can't build insn arg from encoded value: " + encodedValue);
}
}
......@@ -18,6 +18,7 @@ import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.JadxVisitor;
import jadx.core.dex.visitors.OverrideMethodVisitor;
import jadx.core.dex.visitors.RenameVisitor;
import jadx.core.utils.input.InsnDataUtils;
@JadxVisitor(
name = "UsageInfoVisitor",
......@@ -120,18 +121,14 @@ public class UsageInfoVisitor extends AbstractVisitor {
case CALL_SITE: {
insnData.decode();
ICallSite callSite;
ICustomPayload payload = insnData.getPayload();
if (payload != null) {
callSite = ((ICallSite) payload);
} else {
callSite = insnData.getIndexAsCallSite();
}
IMethodHandle methodHandle = (IMethodHandle) callSite.getValues().get(4).getValue();
IMethodRef mthRef = methodHandle.getMethodRef();
MethodNode mthNode = root.resolveMethod(MethodInfo.fromRef(root, mthRef));
if (mthNode != null) {
usageInfo.methodUse(mth, mthNode);
ICallSite callSite = InsnDataUtils.getCallSite(insnData);
IMethodHandle methodHandle = InsnDataUtils.getMethodHandleAt(callSite, 4);
if (methodHandle != null) {
IMethodRef mthRef = methodHandle.getMethodRef();
MethodNode mthNode = root.resolveMethod(MethodInfo.fromRef(root, mthRef));
if (mthNode != null) {
usageInfo.methodUse(mth, mthNode);
}
}
break;
}
......
package jadx.core.utils.input;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import jadx.api.plugins.input.data.ICallSite;
import jadx.api.plugins.input.data.IMethodHandle;
import jadx.api.plugins.input.data.IMethodRef;
import jadx.api.plugins.input.data.annotations.EncodedType;
import jadx.api.plugins.input.data.annotations.EncodedValue;
import jadx.api.plugins.input.insns.InsnData;
import jadx.api.plugins.input.insns.InsnIndexType;
import jadx.api.plugins.input.insns.custom.ICustomPayload;
public class InsnDataUtils {
@Nullable
public static ICallSite getCallSite(InsnData insnData) {
if (insnData.getIndexType() != InsnIndexType.CALL_SITE) {
return null;
}
ICustomPayload payload = insnData.getPayload();
if (payload != null) {
return ((ICallSite) payload);
}
return insnData.getIndexAsCallSite();
}
@Nullable
public static IMethodRef getMethodRef(InsnData insnData) {
if (insnData.getIndexType() != InsnIndexType.METHOD_REF) {
return null;
}
ICustomPayload payload = insnData.getPayload();
if (payload != null) {
return ((IMethodRef) payload);
}
return insnData.getIndexAsMethod();
}
@Nullable
public static IMethodHandle getMethodHandleAt(ICallSite callSite, int argNum) {
if (callSite == null) {
return null;
}
List<EncodedValue> values = callSite.getValues();
if (argNum < values.size()) {
EncodedValue encodedValue = values.get(argNum);
if (encodedValue.getType() == EncodedType.ENCODED_METHOD_HANDLE) {
return (IMethodHandle) encodedValue.getValue();
}
}
return null;
}
}
......@@ -224,7 +224,7 @@ public abstract class IntegrationTest extends TestUtils {
}
System.out.println("-----------------------------------------------------------");
if (printDisassemble) {
clsList.forEach(this::printSmali);
clsList.forEach(this::printDisasm);
}
runChecks(clsList);
}
......@@ -239,7 +239,7 @@ public abstract class IntegrationTest extends TestUtils {
clsList.forEach(this::runAutoCheck);
}
private void printSmali(ClassNode cls) {
private void printDisasm(ClassNode cls) {
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
System.out.println(cls.getDisassembledCode());
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
......
package jadx.tests.integration.others;
import org.junit.jupiter.api.Test;
import jadx.tests.api.RaungTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestStringConcatJava11 extends RaungTest {
public static class TestCls {
public String test(final String s) {
return s + "test";
}
//@formatter:off
/* Dynamic call looks like this:
public String test(final String s) {
return java.lang.invoke.StringConcatFactory.makeConcatWithConstants(
java.lang.invoke.MethodHandles.lookup(),
"makeConcatWithConstants",
java.lang.invoke.MethodType.fromMethodDescriptorString("(Ljava/lang/String;)Ljava/lang/String;", this.getClass().getClassLoader()),
"\u0001test"
).dynamicInvoker().invoke(s);
}
*/
//@formatter:on
public String test2(final String s) {
return s + "test" + s + 7;
}
}
@Test
public void test() {
assertThat(getClassNodeFromRaung())
.code()
.containsOne("return str + \"test\";")
.containsOne("return str + \"test\" + str + 7;");
}
@Test
public void testJava() {
noDebugInfo();
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("return str + \"test\";")
.containsOneOf(
"return str + \"test\" + str + 7;",
"return str + \"test\" + str + \"7\";"); // dynamic concat add const to string recipe
}
}
.version 55
.class public others/TestStringConcatJava11
.method public test(Ljava/lang/String;)Ljava/lang/String;
.max stack 1
.max locals 2
aload 1
invokedynamic makeConcatWithConstants (Ljava/lang/String;)Ljava/lang/String;
.handle invoke-static java/lang/invoke/StringConcatFactory makeConcatWithConstants (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
.arg 0 "\u0001test"
.end invokedynamic
areturn
.end method
.method public test2(Ljava/lang/String;)Ljava/lang/String;
.max stack 2
.max locals 2
aload 1
aload 1
invokedynamic makeConcatWithConstants (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
.handle invoke-static java/lang/invoke/StringConcatFactory makeConcatWithConstants (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
.arg 0 "\u0001test\u0001\u0002"
.arg 1 7 # synthetic usage, compiler adds const values into recipe string
.end invokedynamic
areturn
.end method
......@@ -6,6 +6,5 @@ dependencies {
api(project(":jadx-plugins:jadx-plugins-api"))
// show bytecode disassemble
implementation 'org.ow2.asm:asm:9.2'
implementation 'org.ow2.asm:asm-util:9.2'
implementation 'io.github.skylot:raung-disasm:0.0.2'
}
package jadx.plugins.input.java.utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.TimeUnit;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.util.TraceClassVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.skylot.raung.disasm.RaungDisasm;
public class DisasmUtils {
private static final Logger LOG = LoggerFactory.getLogger(DisasmUtils.class);
public static String get(byte[] bytes) {
return useASM(bytes);
return useRaung(bytes);
}
private static String useASM(byte[] bytes) {
StringWriter out = new StringWriter();
TraceClassVisitor tcv = new TraceClassVisitor(new PrintWriter(out));
new ClassReader(bytes).accept(tcv, 0);
return out.toString();
private static String useRaung(byte[] bytes) {
return RaungDisasm.create()
.executeForInputStream(new ByteArrayInputStream(bytes));
}
/**
......
......@@ -7,5 +7,5 @@ dependencies {
implementation(project(":jadx-plugins:jadx-java-input"))
implementation('io.github.skylot:raung-asm:0.0.1')
implementation('io.github.skylot:raung-asm:0.0.2')
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册