Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
keyescgm
jadx
提交
358cddd9
J
jadx
项目概览
keyescgm
/
jadx
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
jadx
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
358cddd9
编写于
10月 15, 2021
作者:
S
Skylot
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: support dynamic strings concat (#1250)
上级
418df2fd
变更
12
隐藏空白更改
内联
并排
Showing
12 changed file
with
459 addition
and
193 deletion
+459
-193
jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java
...src/main/java/jadx/core/dex/instructions/InsnDecoder.java
+5
-6
jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java
.../java/jadx/core/dex/instructions/InvokeCustomBuilder.java
+12
-160
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomLambdaCall.java
...core/dex/instructions/invokedynamic/CustomLambdaCall.java
+169
-0
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomStringConcat.java
...re/dex/instructions/invokedynamic/CustomStringConcat.java
+117
-0
jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java
...n/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java
+9
-12
jadx-core/src/main/java/jadx/core/utils/input/InsnDataUtils.java
...re/src/main/java/jadx/core/utils/input/InsnDataUtils.java
+56
-0
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
+2
-2
jadx-core/src/test/java/jadx/tests/integration/others/TestStringConcatJava11.java
...jadx/tests/integration/others/TestStringConcatJava11.java
+52
-0
jadx-core/src/test/raung/others/TestStringConcatJava11.raung
jadx-core/src/test/raung/others/TestStringConcatJava11.raung
+28
-0
jadx-plugins/jadx-java-input/build.gradle
jadx-plugins/jadx-java-input/build.gradle
+1
-2
jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/DisasmUtils.java
.../main/java/jadx/plugins/input/java/utils/DisasmUtils.java
+7
-10
jadx-plugins/jadx-raung-input/build.gradle
jadx-plugins/jadx-raung-input/build.gradle
+1
-1
未找到文件。
jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java
浏览文件 @
358cddd9
...
...
@@ -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
);
...
...
jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java
浏览文件 @
358cddd9
...
...
@@ -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
);
}
}
}
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomLambdaCall.java
0 → 100644
浏览文件 @
358cddd9
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
);
}
}
}
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomStringConcat.java
0 → 100644
浏览文件 @
358cddd9
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
);
}
}
jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java
浏览文件 @
358cddd9
...
...
@@ -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
;
}
...
...
jadx-core/src/main/java/jadx/core/utils/input/InsnDataUtils.java
0 → 100644
浏览文件 @
358cddd9
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
;
}
}
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
浏览文件 @
358cddd9
...
...
@@ -224,7 +224,7 @@ public abstract class IntegrationTest extends TestUtils {
}
System
.
out
.
println
(
"-----------------------------------------------------------"
);
if
(
printDisassemble
)
{
clsList
.
forEach
(
this
::
print
Smali
);
clsList
.
forEach
(
this
::
print
Disasm
);
}
runChecks
(
clsList
);
}
...
...
@@ -239,7 +239,7 @@ public abstract class IntegrationTest extends TestUtils {
clsList
.
forEach
(
this
::
runAutoCheck
);
}
private
void
print
Smali
(
ClassNode
cls
)
{
private
void
print
Disasm
(
ClassNode
cls
)
{
System
.
out
.
println
(
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
);
System
.
out
.
println
(
cls
.
getDisassembledCode
());
System
.
out
.
println
(
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
);
...
...
jadx-core/src/test/java/jadx/tests/integration/others/TestStringConcatJava11.java
0 → 100644
浏览文件 @
358cddd9
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
}
}
jadx-core/src/test/raung/others/TestStringConcatJava11.raung
0 → 100644
浏览文件 @
358cddd9
.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
jadx-plugins/jadx-java-input/build.gradle
浏览文件 @
358cddd9
...
...
@@ -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'
}
jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/utils/DisasmUtils.java
浏览文件 @
358cddd9
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
use
ASM
(
bytes
);
return
use
Raung
(
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
));
}
/**
...
...
jadx-plugins/jadx-raung-input/build.gradle
浏览文件 @
358cddd9
...
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录