Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
keyescgm
jadx
提交
85c2c63a
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,体验更适合开发者的 AI 搜索 >>
未验证
提交
85c2c63a
编写于
2月 11, 2023
作者:
S
Skylot
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: output unknown `invoke-custom` as polymorphic call (#1760)
上级
f354f7de
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
541 addition
and
48 deletion
+541
-48
jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
+31
-0
jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java
.../java/jadx/core/dex/instructions/InvokeCustomBuilder.java
+15
-2
jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomRawNode.java
.../java/jadx/core/dex/instructions/InvokeCustomRawNode.java
+98
-0
jadx-core/src/main/java/jadx/core/dex/instructions/InvokeType.java
.../src/main/java/jadx/core/dex/instructions/InvokeType.java
+1
-0
jadx-core/src/main/java/jadx/core/dex/instructions/args/PrimitiveType.java
...n/java/jadx/core/dex/instructions/args/PrimitiveType.java
+18
-12
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomLambdaCall.java
...core/dex/instructions/invokedynamic/CustomLambdaCall.java
+7
-19
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomRawCall.java
...dx/core/dex/instructions/invokedynamic/CustomRawCall.java
+70
-0
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/InvokeCustomUtils.java
...ore/dex/instructions/invokedynamic/InvokeCustomUtils.java
+25
-0
jadx-core/src/main/java/jadx/core/utils/EncodedValueUtils.java
...core/src/main/java/jadx/core/utils/EncodedValueUtils.java
+126
-0
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
+19
-10
jadx-core/src/test/java/jadx/tests/integration/invoke/TestRawCustomInvoke.java
...va/jadx/tests/integration/invoke/TestRawCustomInvoke.java
+66
-0
jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaExtVar.java
...t/java/jadx/tests/integration/java8/TestLambdaExtVar.java
+0
-2
jadx-core/src/test/smali/invoke/TestRawCustomInvoke.smali
jadx-core/src/test/smali/invoke/TestRawCustomInvoke.smali
+62
-0
jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/annotations/EncodedValue.java
...jadx/api/plugins/input/data/annotations/EncodedValue.java
+3
-3
未找到文件。
jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
浏览文件 @
85c2c63a
...
...
@@ -14,6 +14,7 @@ import jadx.api.ICodeWriter;
import
jadx.api.metadata.annotations.InsnCodeOffset
;
import
jadx.api.metadata.annotations.VarNode
;
import
jadx.api.plugins.input.data.MethodHandleType
;
import
jadx.api.plugins.input.data.annotations.EncodedValue
;
import
jadx.core.dex.attributes.AFlag
;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.attributes.nodes.FieldReplaceAttr
;
...
...
@@ -36,6 +37,7 @@ import jadx.core.dex.instructions.IfNode;
import
jadx.core.dex.instructions.IndexInsnNode
;
import
jadx.core.dex.instructions.InsnType
;
import
jadx.core.dex.instructions.InvokeCustomNode
;
import
jadx.core.dex.instructions.InvokeCustomRawNode
;
import
jadx.core.dex.instructions.InvokeNode
;
import
jadx.core.dex.instructions.InvokeType
;
import
jadx.core.dex.instructions.NewArrayNode
;
...
...
@@ -795,6 +797,10 @@ public class InsnGen {
MethodInfo
callMth
=
insn
.
getCallMth
();
MethodNode
callMthNode
=
mth
.
root
().
resolveMethod
(
callMth
);
if
(
type
==
InvokeType
.
CUSTOM_RAW
)
{
makeInvokeCustomRaw
((
InvokeCustomRawNode
)
insn
,
callMthNode
,
code
);
return
;
}
if
(
insn
.
isPolymorphicCall
())
{
// add missing cast
code
.
add
(
'('
);
...
...
@@ -845,6 +851,31 @@ public class InsnGen {
generateMethodArguments
(
code
,
insn
,
k
,
callMthNode
);
}
private
void
makeInvokeCustomRaw
(
InvokeCustomRawNode
insn
,
@Nullable
MethodNode
callMthNode
,
ICodeWriter
code
)
throws
CodegenException
{
if
(
isFallback
())
{
code
.
add
(
"call_site("
);
code
.
incIndent
();
for
(
EncodedValue
value
:
insn
.
getCallSiteValues
())
{
code
.
startLine
(
value
.
toString
());
}
code
.
decIndent
();
code
.
startLine
(
").invoke"
);
generateMethodArguments
(
code
,
insn
,
0
,
callMthNode
);
}
else
{
ArgType
returnType
=
insn
.
getCallMth
().
getReturnType
();
if
(!
returnType
.
isVoid
())
{
code
.
add
(
'('
);
useType
(
code
,
returnType
);
code
.
add
(
") "
);
}
makeInvoke
(
insn
.
getResolveInvoke
(),
code
);
code
.
add
(
".dynamicInvoker().invoke"
);
generateMethodArguments
(
code
,
insn
,
0
,
callMthNode
);
code
.
add
(
" /* invoke-custom */"
);
}
}
// FIXME: add 'this' for equals methods in scope
private
boolean
needInvokeArg
(
InsnArg
arg
)
{
if
(
arg
.
isAnyThis
())
{
...
...
jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomBuilder.java
浏览文件 @
85c2c63a
...
...
@@ -5,10 +5,15 @@ import java.util.List;
import
jadx.api.plugins.input.data.ICallSite
;
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.invokedynamic.CustomLambdaCall
;
import
jadx.core.dex.instructions.invokedynamic.CustomRawCall
;
import
jadx.core.dex.instructions.invokedynamic.CustomStringConcat
;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.utils.Utils
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
import
jadx.core.utils.input.InsnDataUtils
;
...
...
@@ -28,8 +33,16 @@ public class InvokeCustomBuilder {
if
(
CustomStringConcat
.
isStringConcat
(
values
))
{
return
CustomStringConcat
.
buildStringConcat
(
insn
,
isRange
,
values
);
}
// TODO: output raw dynamic call
throw
new
JadxRuntimeException
(
"Failed to process invoke-custom instruction: "
+
callSite
);
try
{
return
CustomRawCall
.
build
(
mth
,
insn
,
isRange
,
values
);
}
catch
(
Exception
e
)
{
mth
.
addWarn
(
"Failed to decode invoke-custom: \n"
+
Utils
.
listToString
(
values
,
"\n"
)
+
",\n exception: "
+
Utils
.
getStackTrace
(
e
));
InsnNode
nop
=
new
InsnNode
(
InsnType
.
NOP
,
0
);
nop
.
add
(
AFlag
.
SYNTHETIC
);
nop
.
addAttr
(
AType
.
JADX_ERROR
,
new
JadxError
(
"Failed to decode invoke-custom: "
+
values
,
e
));
return
nop
;
}
}
catch
(
Exception
e
)
{
throw
new
JadxRuntimeException
(
"'invoke-custom' instruction processing error: "
+
e
.
getMessage
(),
e
);
}
...
...
jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomRawNode.java
0 → 100644
浏览文件 @
85c2c63a
package
jadx.core.dex.instructions
;
import
java.util.List
;
import
org.jetbrains.annotations.Nullable
;
import
jadx.api.plugins.input.data.annotations.EncodedValue
;
import
jadx.api.plugins.input.insns.InsnData
;
import
jadx.core.dex.info.MethodInfo
;
import
jadx.core.dex.instructions.args.InsnArg
;
import
jadx.core.dex.instructions.invokedynamic.CustomRawCall
;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.utils.InsnUtils
;
import
jadx.core.utils.Utils
;
/**
* Information for raw invoke-custom instruction.<br>
* Output will be formatted as polymorphic call with equivalent semantic
* Contains two parts:
* - resolve: treated as additional invoke insn (uses only constant args)
* - invoke: call of resolved method (base for this invoke)
* <br>
* See {@link CustomRawCall} class for build details
*/
public
class
InvokeCustomRawNode
extends
InvokeNode
{
private
final
InvokeNode
resolve
;
private
List
<
EncodedValue
>
callSiteValues
;
public
InvokeCustomRawNode
(
InvokeNode
resolve
,
MethodInfo
mthInfo
,
InsnData
insn
,
boolean
isRange
)
{
super
(
mthInfo
,
insn
,
InvokeType
.
CUSTOM_RAW
,
false
,
isRange
);
this
.
resolve
=
resolve
;
}
public
InvokeCustomRawNode
(
InvokeNode
resolve
,
MethodInfo
mthInfo
,
InvokeType
invokeType
,
int
argsCount
)
{
super
(
mthInfo
,
invokeType
,
argsCount
);
this
.
resolve
=
resolve
;
}
public
InvokeNode
getResolveInvoke
()
{
return
resolve
;
}
public
void
setCallSiteValues
(
List
<
EncodedValue
>
callSiteValues
)
{
this
.
callSiteValues
=
callSiteValues
;
}
public
List
<
EncodedValue
>
getCallSiteValues
()
{
return
callSiteValues
;
}
@Override
public
InsnNode
copy
()
{
InvokeCustomRawNode
copy
=
new
InvokeCustomRawNode
(
resolve
,
getCallMth
(),
getInvokeType
(),
getArgsCount
());
copyCommonParams
(
copy
);
copy
.
setCallSiteValues
(
callSiteValues
);
return
copy
;
}
@Override
public
boolean
isStaticCall
()
{
return
true
;
}
@Override
public
int
getFirstArgOffset
()
{
return
0
;
}
@Override
public
@Nullable
InsnArg
getInstanceArg
()
{
return
null
;
}
@Override
public
boolean
isSame
(
InsnNode
obj
)
{
if
(
this
==
obj
)
{
return
true
;
}
if
(
obj
instanceof
InvokeCustomRawNode
)
{
return
super
.
isSame
(
obj
)
&&
resolve
.
isSame
(((
InvokeCustomRawNode
)
obj
).
resolve
);
}
return
false
;
}
@Override
public
String
toString
()
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
InsnUtils
.
formatOffset
(
offset
)).
append
(
": INVOKE_CUSTOM "
);
if
(
getResult
()
!=
null
)
{
sb
.
append
(
getResult
()).
append
(
" = "
);
}
if
(!
appendArgs
(
sb
))
{
sb
.
append
(
'\n'
);
}
sb
.
append
(
" call-site: \n "
).
append
(
Utils
.
listToString
(
callSiteValues
,
"\n "
)).
append
(
'\n'
);
return
sb
.
toString
();
}
}
jadx-core/src/main/java/jadx/core/dex/instructions/InvokeType.java
浏览文件 @
85c2c63a
...
...
@@ -8,4 +8,5 @@ public enum InvokeType {
SUPER
,
POLYMORPHIC
,
CUSTOM
,
CUSTOM_RAW
,
}
jadx-core/src/main/java/jadx/core/dex/instructions/args/PrimitiveType.java
浏览文件 @
85c2c63a
package
jadx.core.dex.instructions.args
;
public
enum
PrimitiveType
{
BOOLEAN
(
"Z"
,
"boolean"
),
CHAR
(
"C"
,
"char"
),
BYTE
(
"B"
,
"byte"
),
SHORT
(
"S"
,
"short"
),
INT
(
"I"
,
"int"
),
FLOAT
(
"F"
,
"float"
),
LONG
(
"J"
,
"long"
),
DOUBLE
(
"D"
,
"double"
),
OBJECT
(
"L"
,
"OBJECT"
),
ARRAY
(
"["
,
"ARRAY"
),
VOID
(
"V"
,
"void"
);
BOOLEAN
(
"Z"
,
"boolean"
,
ArgType
.
object
(
"java.lang.Boolean"
)
),
CHAR
(
"C"
,
"char"
,
ArgType
.
object
(
"java.lang.Character"
)
),
BYTE
(
"B"
,
"byte"
,
ArgType
.
object
(
"java.lang.Byte"
)
),
SHORT
(
"S"
,
"short"
,
ArgType
.
object
(
"java.lang.Short"
)
),
INT
(
"I"
,
"int"
,
ArgType
.
object
(
"java.lang.Integer"
)
),
FLOAT
(
"F"
,
"float"
,
ArgType
.
object
(
"java.lang.Float"
)
),
LONG
(
"J"
,
"long"
,
ArgType
.
object
(
"java.lang.Long"
)
),
DOUBLE
(
"D"
,
"double"
,
ArgType
.
object
(
"java.lang.Double"
)
),
OBJECT
(
"L"
,
"OBJECT"
,
ArgType
.
OBJECT
),
ARRAY
(
"["
,
"ARRAY"
,
ArgType
.
OBJECT_ARRAY
),
VOID
(
"V"
,
"void"
,
ArgType
.
object
(
"java.lang.Void"
)
);
private
final
String
shortName
;
private
final
String
longName
;
private
final
ArgType
boxType
;
PrimitiveType
(
String
shortName
,
String
longName
)
{
PrimitiveType
(
String
shortName
,
String
longName
,
ArgType
boxType
)
{
this
.
shortName
=
shortName
;
this
.
longName
=
longName
;
this
.
boxType
=
boxType
;
}
public
String
getShortName
()
{
...
...
@@ -29,6 +31,10 @@ public enum PrimitiveType {
return
longName
;
}
public
ArgType
getBoxType
()
{
return
boxType
;
}
@Override
public
String
toString
()
{
return
longName
;
...
...
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/CustomLambdaCall.java
浏览文件 @
85c2c63a
...
...
@@ -8,6 +8,7 @@ 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.EncodedType
;
import
jadx.api.plugins.input.data.annotations.EncodedValue
;
import
jadx.api.plugins.input.insns.InsnData
;
import
jadx.core.dex.attributes.AFlag
;
...
...
@@ -34,7 +35,11 @@ public class CustomLambdaCall {
if
(
values
.
size
()
<
6
)
{
return
false
;
}
IMethodHandle
methodHandle
=
(
IMethodHandle
)
values
.
get
(
0
).
getValue
();
EncodedValue
mthRef
=
values
.
get
(
0
);
if
(
mthRef
.
getType
()
!=
EncodedType
.
ENCODED_METHOD_HANDLE
)
{
return
false
;
}
IMethodHandle
methodHandle
=
(
IMethodHandle
)
mthRef
.
getValue
();
if
(
methodHandle
.
getType
()
!=
MethodHandleType
.
INVOKE_STATIC
)
{
return
false
;
}
...
...
@@ -113,7 +118,7 @@ public class CustomLambdaCall {
@NotNull
private
static
InvokeNode
buildInvokeNode
(
MethodHandleType
methodHandleType
,
InvokeCustomNode
invokeCustomNode
,
MethodInfo
callMthInfo
)
{
InvokeType
invokeType
=
convertInvokeType
(
methodHandleType
);
InvokeType
invokeType
=
InvokeCustomUtils
.
convertInvokeType
(
methodHandleType
);
int
callArgsCount
=
callMthInfo
.
getArgsCount
();
boolean
instanceCall
=
invokeType
!=
InvokeType
.
STATIC
;
if
(
instanceCall
)
{
...
...
@@ -147,21 +152,4 @@ public class CustomLambdaCall {
}
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/CustomRawCall.java
0 → 100644
浏览文件 @
85c2c63a
package
jadx.core.dex.instructions.invokedynamic
;
import
java.util.ArrayList
;
import
java.util.List
;
import
jadx.api.plugins.input.data.IMethodHandle
;
import
jadx.api.plugins.input.data.IMethodProto
;
import
jadx.api.plugins.input.data.annotations.EncodedValue
;
import
jadx.api.plugins.input.insns.InsnData
;
import
jadx.core.dex.info.ClassInfo
;
import
jadx.core.dex.info.MethodInfo
;
import
jadx.core.dex.instructions.ConstStringNode
;
import
jadx.core.dex.instructions.InvokeCustomRawNode
;
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.nodes.InsnNode
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.nodes.RootNode
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
import
static
jadx
.
core
.
utils
.
EncodedValueUtils
.
buildLookupArg
;
import
static
jadx
.
core
.
utils
.
EncodedValueUtils
.
convertToInsnArg
;
/**
* Show `invoke-custom` similar to polymorphic call
*/
public
class
CustomRawCall
{
public
static
InsnNode
build
(
MethodNode
mth
,
InsnData
insn
,
boolean
isRange
,
List
<
EncodedValue
>
values
)
{
IMethodHandle
resolveHandle
=
(
IMethodHandle
)
values
.
get
(
0
).
getValue
();
String
invokeName
=
(
String
)
values
.
get
(
1
).
getValue
();
IMethodProto
invokeProto
=
(
IMethodProto
)
values
.
get
(
2
).
getValue
();
List
<
InsnArg
>
resolveArgs
=
buildArgs
(
mth
,
values
);
if
(
resolveHandle
.
getType
().
isField
())
{
throw
new
JadxRuntimeException
(
"Field handle not yet supported"
);
}
RootNode
root
=
mth
.
root
();
MethodInfo
resolveMth
=
MethodInfo
.
fromRef
(
root
,
resolveHandle
.
getMethodRef
());
InvokeType
resolveInvokeType
=
InvokeCustomUtils
.
convertInvokeType
(
resolveHandle
.
getType
());
InvokeNode
resolve
=
new
InvokeNode
(
resolveMth
,
resolveInvokeType
,
resolveArgs
.
size
());
resolveArgs
.
forEach
(
resolve:
:
addArg
);
ClassInfo
invokeCls
=
ClassInfo
.
fromType
(
root
,
ArgType
.
OBJECT
);
// type will be known at runtime
MethodInfo
invokeMth
=
MethodInfo
.
fromMethodProto
(
root
,
invokeCls
,
invokeName
,
invokeProto
);
InvokeCustomRawNode
customRawNode
=
new
InvokeCustomRawNode
(
resolve
,
invokeMth
,
insn
,
isRange
);
customRawNode
.
setCallSiteValues
(
values
);
return
customRawNode
;
}
private
static
List
<
InsnArg
>
buildArgs
(
MethodNode
mth
,
List
<
EncodedValue
>
values
)
{
int
valuesCount
=
values
.
size
();
List
<
InsnArg
>
list
=
new
ArrayList
<>(
valuesCount
);
RootNode
root
=
mth
.
root
();
list
.
add
(
buildLookupArg
(
root
));
// use `java.lang.invoke.MethodHandles.lookup()` as first arg
for
(
int
i
=
1
;
i
<
valuesCount
;
i
++)
{
EncodedValue
value
=
values
.
get
(
i
);
try
{
list
.
add
(
convertToInsnArg
(
root
,
value
));
}
catch
(
Exception
e
)
{
mth
.
addWarnComment
(
"Failed to build arg in invoke-custom insn: "
+
value
,
e
);
list
.
add
(
InsnArg
.
wrapArg
(
new
ConstStringNode
(
value
.
toString
())));
}
}
return
list
;
}
}
jadx-core/src/main/java/jadx/core/dex/instructions/invokedynamic/InvokeCustomUtils.java
0 → 100644
浏览文件 @
85c2c63a
package
jadx.core.dex.instructions.invokedynamic
;
import
jadx.api.plugins.input.data.MethodHandleType
;
import
jadx.core.dex.instructions.InvokeType
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
public
class
InvokeCustomUtils
{
public
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/utils/EncodedValueUtils.java
浏览文件 @
85c2c63a
package
jadx.core.utils
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.List
;
import
org.jetbrains.annotations.Nullable
;
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.core.dex.info.ClassInfo
;
import
jadx.core.dex.info.FieldInfo
;
import
jadx.core.dex.info.MethodInfo
;
import
jadx.core.dex.instructions.ConstClassNode
;
import
jadx.core.dex.instructions.ConstStringNode
;
import
jadx.core.dex.instructions.IndexInsnNode
;
import
jadx.core.dex.instructions.InsnType
;
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.LiteralArg
;
import
jadx.core.dex.instructions.args.PrimitiveType
;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.dex.nodes.RootNode
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
public
class
EncodedValueUtils
{
...
...
@@ -50,4 +72,108 @@ public class EncodedValueUtils {
return
null
;
}
}
public
static
InsnArg
convertToInsnArg
(
RootNode
root
,
EncodedValue
value
)
{
Object
obj
=
value
.
getValue
();
switch
(
value
.
getType
())
{
case
ENCODED_NULL:
case
ENCODED_BYTE:
case
ENCODED_SHORT:
case
ENCODED_CHAR:
case
ENCODED_INT:
case
ENCODED_LONG:
case
ENCODED_FLOAT:
case
ENCODED_DOUBLE:
return
(
InsnArg
)
convertToConstValue
(
value
);
case
ENCODED_BOOLEAN:
return
InsnArg
.
lit
(((
Boolean
)
obj
)
?
0
:
1
,
ArgType
.
BOOLEAN
);
case
ENCODED_STRING:
return
InsnArg
.
wrapArg
(
new
ConstStringNode
((
String
)
obj
));
case
ENCODED_TYPE:
return
InsnArg
.
wrapArg
(
new
ConstClassNode
(
ArgType
.
parse
((
String
)
obj
)));
case
ENCODED_METHOD_TYPE:
return
InsnArg
.
wrapArg
(
buildMethodType
(
root
,
(
IMethodProto
)
obj
));
case
ENCODED_METHOD_HANDLE:
return
InsnArg
.
wrapArg
(
buildMethodHandle
(
root
,
(
IMethodHandle
)
obj
));
}
throw
new
JadxRuntimeException
(
"Unsupported type for raw invoke-custom: "
+
value
.
getType
());
}
private
static
InvokeNode
buildMethodType
(
RootNode
root
,
IMethodProto
methodProto
)
{
ArgType
retType
=
ArgType
.
parse
(
methodProto
.
getReturnType
());
List
<
ArgType
>
argTypes
=
Utils
.
collectionMap
(
methodProto
.
getArgTypes
(),
ArgType:
:
parse
);
List
<
ArgType
>
callTypes
=
new
ArrayList
<>(
1
+
argTypes
.
size
());
callTypes
.
add
(
retType
);
callTypes
.
addAll
(
argTypes
);
ArgType
mthType
=
ArgType
.
object
(
"java.lang.invoke.MethodType"
);
ClassInfo
cls
=
ClassInfo
.
fromType
(
root
,
mthType
);
MethodInfo
mth
=
MethodInfo
.
fromDetails
(
root
,
cls
,
"methodType"
,
callTypes
,
mthType
);
InvokeNode
invoke
=
new
InvokeNode
(
mth
,
InvokeType
.
STATIC
,
callTypes
.
size
());
for
(
ArgType
type
:
callTypes
)
{
InsnNode
argInsn
;
if
(
type
.
isPrimitive
())
{
argInsn
=
new
IndexInsnNode
(
InsnType
.
SGET
,
getTypeField
(
root
,
type
.
getPrimitiveType
()),
0
);
}
else
{
argInsn
=
new
ConstClassNode
(
type
);
}
invoke
.
addArg
(
InsnArg
.
wrapArg
(
argInsn
));
}
return
invoke
;
}
public
static
FieldInfo
getTypeField
(
RootNode
root
,
PrimitiveType
type
)
{
ArgType
boxType
=
type
.
getBoxType
();
ClassInfo
boxCls
=
ClassInfo
.
fromType
(
root
,
boxType
);
return
FieldInfo
.
from
(
root
,
boxCls
,
"TYPE"
,
boxType
);
}
/**
* Build `MethodHandles.lookup().find{type}(methodCls, methodName, methodType)`
*/
private
static
InsnNode
buildMethodHandle
(
RootNode
root
,
IMethodHandle
methodHandle
)
{
if
(
methodHandle
.
getType
().
isField
())
{
// TODO: lookup for field
return
new
ConstStringNode
(
"FIELD:"
+
methodHandle
.
getFieldRef
());
}
IMethodRef
methodRef
=
methodHandle
.
getMethodRef
();
methodRef
.
load
();
ClassInfo
lookupCls
=
ClassInfo
.
fromName
(
root
,
"java.lang.invoke.MethodHandles.Lookup"
);
MethodInfo
findMethod
=
MethodInfo
.
fromDetails
(
root
,
lookupCls
,
getFindMethodName
(
methodHandle
.
getType
()),
Arrays
.
asList
(
ArgType
.
CLASS
,
ArgType
.
STRING
,
ArgType
.
object
(
"java.lang.invoke.MethodType"
)),
ArgType
.
object
(
"java.lang.invoke.MethodHandle"
));
InvokeNode
invoke
=
new
InvokeNode
(
findMethod
,
InvokeType
.
DIRECT
,
4
);
invoke
.
addArg
(
buildLookupArg
(
root
));
invoke
.
addArg
(
InsnArg
.
wrapArg
(
new
ConstClassNode
(
ArgType
.
object
(
methodRef
.
getParentClassType
()))));
invoke
.
addArg
(
InsnArg
.
wrapArg
(
new
ConstStringNode
(
methodRef
.
getName
())));
invoke
.
addArg
(
InsnArg
.
wrapArg
(
buildMethodType
(
root
,
methodRef
)));
return
invoke
;
}
public
static
InsnArg
buildLookupArg
(
RootNode
root
)
{
ArgType
lookupType
=
ArgType
.
object
(
"java.lang.invoke.MethodHandles.Lookup"
);
ClassInfo
cls
=
ClassInfo
.
fromName
(
root
,
"java.lang.invoke.MethodHandles"
);
MethodInfo
mth
=
MethodInfo
.
fromDetails
(
root
,
cls
,
"lookup"
,
Collections
.
emptyList
(),
lookupType
);
return
InsnArg
.
wrapArg
(
new
InvokeNode
(
mth
,
InvokeType
.
STATIC
,
0
));
}
private
static
String
getFindMethodName
(
MethodHandleType
type
)
{
switch
(
type
)
{
case
INVOKE_STATIC:
return
"findStatic"
;
case
INVOKE_CONSTRUCTOR:
return
"findConstructor"
;
case
INVOKE_INSTANCE:
case
INVOKE_DIRECT:
case
INVOKE_INTERFACE:
return
"findVirtual"
;
default
:
return
"<"
+
type
+
'>'
;
}
}
}
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
浏览文件 @
85c2c63a
...
...
@@ -110,6 +110,12 @@ public abstract class IntegrationTest extends TestUtils {
private
@Nullable
TestCompiler
sourceCompiler
;
private
@Nullable
TestCompiler
decompiledCompiler
;
/**
* Run check method on decompiled code even if source check method not found.
* Useful for smali test if check method added to smali code
*/
private
boolean
forceDecompiledCheck
=
false
;
static
{
// enable debug checks
DebugChecks
.
checksEnabled
=
true
;
...
...
@@ -347,11 +353,10 @@ public abstract class IntegrationTest extends TestUtils {
String
clsName
=
cls
.
getClassInfo
().
getRawName
().
replace
(
'/'
,
'.'
);
try
{
// run 'check' method from original class
if
(
runSourceAutoCheck
(
clsName
))
{
return
;
}
boolean
sourceCheckFound
=
runSourceAutoCheck
(
clsName
);
// run 'check' method from decompiled class
if
(
compile
)
{
if
(
compile
&&
(
sourceCheckFound
||
forceDecompiledCheck
)
)
{
runDecompiledAutoCheck
(
cls
);
}
}
catch
(
Exception
e
)
{
...
...
@@ -362,36 +367,36 @@ public abstract class IntegrationTest extends TestUtils {
private
boolean
runSourceAutoCheck
(
String
clsName
)
{
if
(
sourceCompiler
==
null
)
{
// no source code (smali case)
return
tru
e
;
System
.
out
.
println
(
"Source check: no code"
);
return
fals
e
;
}
Class
<?>
origCls
;
try
{
origCls
=
sourceCompiler
.
getClass
(
clsName
);
}
catch
(
ClassNotFoundException
e
)
{
rethrow
(
"Missing class: "
+
clsName
,
e
);
return
tru
e
;
return
fals
e
;
}
Method
checkMth
;
try
{
checkMth
=
sourceCompiler
.
getMethod
(
origCls
,
CHECK_METHOD_NAME
,
new
Class
[]
{});
}
catch
(
NoSuchMethodException
e
)
{
// ignore
return
tru
e
;
return
fals
e
;
}
if
(!
checkMth
.
getReturnType
().
equals
(
void
.
class
)
||
!
Modifier
.
isPublic
(
checkMth
.
getModifiers
())
||
Modifier
.
isStatic
(
checkMth
.
getModifiers
()))
{
fail
(
"Wrong 'check' method"
);
return
tru
e
;
return
fals
e
;
}
try
{
limitExecTime
(()
->
checkMth
.
invoke
(
origCls
.
getConstructor
().
newInstance
()));
System
.
out
.
println
(
"Source check: PASSED"
);
return
true
;
}
catch
(
Throwable
e
)
{
throw
new
JadxRuntimeException
(
"Source check failed"
,
e
);
}
return
false
;
}
public
void
runDecompiledAutoCheck
(
ClassNode
cls
)
{
...
...
@@ -554,6 +559,10 @@ public abstract class IntegrationTest extends TestUtils {
this
.
compile
=
false
;
}
protected
void
forceDecompiledCheck
()
{
this
.
forceDecompiledCheck
=
true
;
}
protected
void
enableDeobfuscation
()
{
args
.
setDeobfuscationOn
(
true
);
args
.
setDeobfuscationMapFileMode
(
DeobfuscationMapFileMode
.
IGNORE
);
...
...
jadx-core/src/test/java/jadx/tests/integration/invoke/TestRawCustomInvoke.java
0 → 100644
浏览文件 @
85c2c63a
package
jadx.tests.integration.invoke
;
import
java.lang.invoke.CallSite
;
import
java.lang.invoke.ConstantCallSite
;
import
java.lang.invoke.MethodHandles
;
import
java.lang.invoke.MethodType
;
import
org.junit.jupiter.api.Test
;
import
jadx.tests.api.SmaliTest
;
import
static
jadx
.
tests
.
api
.
utils
.
assertj
.
JadxAssertions
.
assertThat
;
import
static
org
.
junit
.
jupiter
.
api
.
Assertions
.
fail
;
public
class
TestRawCustomInvoke
extends
SmaliTest
{
public
static
class
TestCls
{
public
static
String
func
(
int
a
,
double
b
)
{
return
String
.
valueOf
(
a
+
b
);
}
private
static
CallSite
staticBootstrap
(
MethodHandles
.
Lookup
lookup
,
String
name
,
MethodType
type
)
{
try
{
return
new
ConstantCallSite
(
lookup
.
findStatic
(
lookup
.
lookupClass
(),
name
,
type
));
}
catch
(
NoSuchMethodException
|
IllegalAccessException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
public
String
test
()
{
try
{
return
(
String
)
staticBootstrap
(
MethodHandles
.
lookup
(),
"func"
,
MethodType
.
methodType
(
String
.
class
,
Integer
.
TYPE
,
Double
.
TYPE
))
.
dynamicInvoker
().
invoke
(
1
,
2.0d
);
}
catch
(
Throwable
e
)
{
fail
(
e
);
return
null
;
}
}
public
void
check
()
{
assertThat
(
test
()).
isEqualTo
(
"3.0"
);
}
}
@Test
public
void
test
()
{
noDebugInfo
();
// this code does not contain `invoke-custom` instruction
// only check if equivalent polymorphic call is correct
assertThat
(
getClassNode
(
TestCls
.
class
))
.
code
()
.
containsOne
(
"return (String) staticBootstrap(MethodHandles.lookup(), \"func\", MethodType.methodType(String.class, Integer.TYPE, Double.TYPE)).dynamicInvoker().invoke(1, 2.0d);"
);
}
@Test
public
void
testSmali
()
{
forceDecompiledCheck
();
assertThat
(
getClassNodeFromSmali
())
.
code
()
.
containsOne
(
"return (String) staticBootstrap(MethodHandles.lookup(), \"func\", MethodType.methodType(String.class, Integer.TYPE, Double.TYPE)).dynamicInvoker().invoke(1, 2.0d) /* invoke-custom */;"
);
}
}
jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaExtVar.java
浏览文件 @
85c2c63a
...
...
@@ -33,7 +33,5 @@ public class TestLambdaExtVar extends IntegrationTest {
.
code
()
.
doesNotContain
(
"lambda$"
)
.
containsOne
(
"return s.equals(str);"
);
// TODO: simplify to expression
System
.
out
.
println
(
cls
.
getCode
().
getCodeMetadata
());
}
}
jadx-core/src/test/smali/invoke/TestRawCustomInvoke.smali
0 → 100644
浏览文件 @
85c2c63a
.class public Linvoke/TestRawCustomInvoke;
.super Ljava/lang/Object;
.method public static func(ID)Ljava/lang/String;
.registers 5
int-to-double v0, p0
add-double/2addr v0, p1
invoke-static {v0, v1}, Ljava/lang/String;->valueOf(D)Ljava/lang/String;
move-result-object p0
return-object p0
.end method
.method private static staticBootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
.registers 5
:try_start_0
new-instance v0, Ljava/lang/invoke/ConstantCallSite;
invoke-virtual {p0}, Ljava/lang/invoke/MethodHandles$Lookup;->lookupClass()Ljava/lang/Class;
move-result-object v1
invoke-virtual {p0, v1, p1, p2}, Ljava/lang/invoke/MethodHandles$Lookup;->findStatic(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
move-result-object p0
invoke-direct {v0, p0}, Ljava/lang/invoke/ConstantCallSite;-><init>(Ljava/lang/invoke/MethodHandle;)V
:try_end_d
.catch Ljava/lang/NoSuchMethodException; {:try_start_0 .. :try_end_d} :catch_e
.catch Ljava/lang/IllegalAccessException; {:try_start_0 .. :try_end_d} :catch_e
return-object v0
:catch_e
move-exception p0
new-instance p1, Ljava/lang/RuntimeException;
invoke-direct {p1, p0}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/Throwable;)V
throw p1
.end method
.method public test()Ljava/lang/String;
.registers 3
:try_start_0
const/4 v0, 0x1
const-wide/high16 v1, 0x4000000000000000L # 2.0
invoke-custom {v0, v1}, call_site_0("func", (ID)Ljava/lang/String;)@Linvoke/TestRawCustomInvoke;->staticBootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
move-result-object v0
:try_end_25
.catchall {:try_start_0 .. :try_end_25} :catchall_26
return-object v0
:catchall_26
move-exception v0
invoke-static {v0}, Lorg/junit/jupiter/api/Assertions;->fail(Ljava/lang/Throwable;)Ljava/lang/Object;
const/4 v0, 0x0
return-object v0
.end method
.method public check()V
.registers 3
invoke-virtual {p0}, Linvoke/TestRawCustomInvoke;->test()Ljava/lang/String;
move-result-object v0
invoke-static {v0}, Ljadx/tests/api/utils/assertj/JadxAssertions;->assertThat(Ljava/lang/String;)Ljadx/tests/api/utils/assertj/JadxCodeAssertions;
move-result-object v0
const-string v1, "3.0"
invoke-virtual {v0, v1}, Ljadx/tests/api/utils/assertj/JadxCodeAssertions;->isEqualTo(Ljava/lang/String;)Lorg/assertj/core/api/AbstractStringAssert;
return-void
.end method
jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/input/data/annotations/EncodedValue.java
浏览文件 @
85c2c63a
...
...
@@ -53,12 +53,12 @@ public class EncodedValue extends PinnedAttribute {
switch
(
type
)
{
case
ENCODED_NULL:
return
"null"
;
case
ENCODED_STRING:
return
(
String
)
value
;
case
ENCODED_ARRAY:
return
"["
+
value
+
"]"
;
case
ENCODED_STRING:
return
"{STRING: \""
+
value
+
"\"}"
;
default
:
return
"{"
+
type
+
": "
+
value
+
'}'
;
return
"{"
+
type
.
toString
().
substring
(
8
)
+
": "
+
value
+
'}'
;
}
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录