Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
J
jadx
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open-source
jadx
Commits
389caf18
Commit
389caf18
authored
Feb 17, 2019
by
Skylot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: improve filled array detection
parent
5cee498e
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
156 additions
and
46 deletions
+156
-46
RegisterArg.java
...ain/java/jadx/core/dex/instructions/args/RegisterArg.java
+10
-16
ReSugarCode.java
...ore/src/main/java/jadx/core/dex/visitors/ReSugarCode.java
+89
-29
InstructionRemover.java
...ore/src/main/java/jadx/core/utils/InstructionRemover.java
+5
-1
Utils.java
jadx-core/src/main/java/jadx/core/utils/Utils.java
+10
-0
TestMultiDimArrayFill.java
.../jadx/tests/integration/arrays/TestMultiDimArrayFill.java
+42
-0
No files found.
jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java
View file @
389caf18
...
...
@@ -5,8 +5,6 @@ import java.util.Objects;
import
org.jetbrains.annotations.NotNull
;
import
org.jetbrains.annotations.Nullable
;
import
jadx.core.dex.instructions.InsnType
;
import
jadx.core.dex.instructions.PhiInsn
;
import
jadx.core.dex.nodes.DexNode
;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.utils.InsnUtils
;
...
...
@@ -115,6 +113,7 @@ public class RegisterArg extends InsnArg implements Named {
}
}
@Override
public
RegisterArg
duplicate
()
{
return
duplicate
(
getRegNum
(),
sVar
);
}
...
...
@@ -130,8 +129,6 @@ public class RegisterArg extends InsnArg implements Named {
/**
* Return constant value from register assign or null if not constant
*
* @return LiteralArg, String or ArgType
*/
public
Object
getConstValue
(
DexNode
dex
)
{
InsnNode
parInsn
=
getAssignInsn
();
...
...
@@ -149,22 +146,19 @@ public class RegisterArg extends InsnArg implements Named {
return
sVar
.
getAssign
().
getParentInsn
();
}
public
InsnNode
getPhiAssignInsn
()
{
PhiInsn
usePhi
=
sVar
.
getUsedInPhi
();
if
(
usePhi
!=
null
)
{
return
usePhi
;
}
InsnNode
parent
=
sVar
.
getAssign
().
getParentInsn
();
if
(
parent
!=
null
&&
parent
.
getType
()
==
InsnType
.
PHI
)
{
return
parent
;
}
return
null
;
}
public
boolean
equalRegister
(
RegisterArg
arg
)
{
return
regNum
==
arg
.
regNum
;
}
public
boolean
sameRegAndSVar
(
InsnArg
arg
)
{
if
(!
arg
.
isRegister
())
{
return
false
;
}
RegisterArg
reg
=
(
RegisterArg
)
arg
;
return
regNum
==
reg
.
getRegNum
()
&&
Objects
.
equals
(
sVar
,
reg
.
getSVar
());
}
public
boolean
equalRegisterAndType
(
RegisterArg
arg
)
{
return
regNum
==
arg
.
regNum
&&
type
.
equals
(
arg
.
type
);
}
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java
View file @
389caf18
package
jadx
.
core
.
dex
.
visitors
;
import
java.util.List
;
import
java.util.stream.Collectors
;
import
org.jetbrains.annotations.Nullable
;
import
org.slf4j.Logger
;
...
...
@@ -21,13 +22,18 @@ import jadx.core.dex.instructions.args.ArgType;
import
jadx.core.dex.instructions.args.InsnArg
;
import
jadx.core.dex.instructions.args.InsnWrapArg
;
import
jadx.core.dex.instructions.args.LiteralArg
;
import
jadx.core.dex.instructions.args.RegisterArg
;
import
jadx.core.dex.instructions.args.SSAVar
;
import
jadx.core.dex.nodes.BlockNode
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.DexNode
;
import
jadx.core.dex.nodes.FieldNode
;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.utils.InsnList
;
import
jadx.core.utils.InsnUtils
;
import
jadx.core.utils.InstructionRemover
;
import
jadx.core.utils.Utils
;
import
jadx.core.utils.exceptions.JadxException
;
@JadxVisitor
(
...
...
@@ -56,65 +62,119 @@ public class ReSugarCode extends AbstractVisitor {
List
<
InsnNode
>
instructions
=
block
.
getInstructions
();
int
size
=
instructions
.
size
();
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
InsnNode
replacedInsn
=
process
(
mth
,
instructions
,
i
,
remover
);
if
(
replacedInsn
!=
null
)
{
instructions
.
set
(
i
,
replacedInsn
);
}
process
(
mth
,
instructions
,
i
,
remover
);
}
remover
.
perform
();
}
}
private
static
InsnNode
process
(
MethodNode
mth
,
List
<
InsnNode
>
instructions
,
int
i
,
InstructionRemover
remover
)
{
private
static
void
process
(
MethodNode
mth
,
List
<
InsnNode
>
instructions
,
int
i
,
InstructionRemover
remover
)
{
InsnNode
insn
=
instructions
.
get
(
i
);
if
(
insn
.
contains
(
AFlag
.
REMOVE
))
{
return
;
}
switch
(
insn
.
getType
())
{
case
NEW_ARRAY:
return
processNewArray
(
mth
,
instructions
,
i
,
remover
);
processNewArray
(
mth
,
instructions
,
i
,
remover
);
break
;
case
SWITCH:
processEnumSwitch
(
mth
,
(
SwitchNode
)
insn
);
return
null
;
break
;
default
:
return
null
;
break
;
}
}
/**
* Replace new array and sequence of array-put to new filled-array instruction.
*/
private
static
InsnNode
processNewArray
(
MethodNode
mth
,
List
<
InsnNode
>
instructions
,
int
i
,
InstructionRemover
remover
)
{
private
static
void
processNewArray
(
MethodNode
mth
,
List
<
InsnNode
>
instructions
,
int
i
,
InstructionRemover
remover
)
{
NewArrayNode
newArrayInsn
=
(
NewArrayNode
)
instructions
.
get
(
i
);
InsnArg
arg
=
newArrayInsn
.
getArg
(
0
);
if
(!
arg
.
isLiteral
())
{
return
null
;
InsnArg
ar
rLenAr
g
=
newArrayInsn
.
getArg
(
0
);
if
(!
ar
rLenAr
g
.
isLiteral
())
{
return
;
}
int
len
=
(
int
)
((
LiteralArg
)
arg
).
getLiteral
();
int
size
=
instructions
.
size
();
if
(
len
<=
0
||
i
+
len
>=
size
||
instructions
.
get
(
i
+
len
).
getType
()
!=
InsnType
.
APUT
)
{
return
null
;
int
len
=
(
int
)
((
LiteralArg
)
arrLenArg
).
getLiteral
();
if
(
len
==
0
)
{
return
;
}
RegisterArg
arrArg
=
newArrayInsn
.
getResult
();
SSAVar
ssaVar
=
arrArg
.
getSVar
();
List
<
RegisterArg
>
useList
=
ssaVar
.
getUseList
();
if
(
useList
.
size
()
<
len
)
{
return
;
}
// check sequential array put with increasing index
int
putIndex
=
0
;
for
(
RegisterArg
useArg
:
useList
)
{
InsnNode
insn
=
useArg
.
getParentInsn
();
if
(
checkPutInsn
(
mth
,
insn
,
arrArg
,
putIndex
))
{
putIndex
++;
}
else
{
break
;
}
}
for
(
int
j
=
0
;
j
<
len
;
j
++)
{
InsnNode
put
=
instructions
.
get
(
i
+
1
+
j
);
if
(
put
.
getType
()
!=
InsnType
.
APUT
)
{
LOG
.
debug
(
"Not a APUT in expected new filled array: {}, method: {}"
,
put
,
mth
);
return
null
;
if
(
putIndex
!=
len
)
{
return
;
}
List
<
InsnNode
>
arrPuts
=
useList
.
subList
(
0
,
len
).
stream
().
map
(
InsnArg:
:
getParentInsn
).
collect
(
Collectors
.
toList
());
// check that all puts in current block
for
(
InsnNode
arrPut
:
arrPuts
)
{
int
index
=
InsnList
.
getIndex
(
instructions
,
arrPut
);
if
(
index
==
-
1
)
{
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"TODO: APUT found in different block: {}, mth: {}"
,
arrPut
,
mth
);
}
return
;
}
}
// checks complete, apply
ArgType
arrType
=
newArrayInsn
.
getArrayType
();
InsnNode
filledArr
=
new
FilledNewArrayNode
(
arrType
.
getArrayElement
(),
len
);
filledArr
.
setResult
(
newArrayInsn
.
getResult
().
duplicate
());
for
(
int
j
=
0
;
j
<
len
;
j
++)
{
InsnNode
put
=
instructions
.
get
(
i
+
1
+
j
);
filledArr
.
setResult
(
arrArg
.
duplicate
());
for
(
InsnNode
put
:
arrPuts
)
{
filledArr
.
addArg
(
put
.
getArg
(
2
).
duplicate
());
remover
.
add
(
put
);
remover
.
addAndUnbind
(
mth
,
put
);
}
remover
.
addAndUnbind
(
mth
,
newArrayInsn
);
int
replaceIndex
=
InsnList
.
getIndex
(
instructions
,
Utils
.
last
(
arrPuts
));
instructions
.
set
(
replaceIndex
,
filledArr
);
}
private
static
boolean
checkPutInsn
(
MethodNode
mth
,
InsnNode
insn
,
RegisterArg
arrArg
,
int
putIndex
)
{
if
(
insn
==
null
||
insn
.
getType
()
!=
InsnType
.
APUT
)
{
return
false
;
}
if
(!
arrArg
.
sameRegAndSVar
(
insn
.
getArg
(
0
)))
{
return
false
;
}
InsnArg
indexArg
=
insn
.
getArg
(
1
);
int
index
=
-
1
;
if
(
indexArg
.
isLiteral
())
{
index
=
(
int
)
((
LiteralArg
)
indexArg
).
getLiteral
();
}
else
if
(
indexArg
.
isRegister
())
{
RegisterArg
reg
=
(
RegisterArg
)
indexArg
;
index
=
getIntConst
(
reg
.
getConstValue
(
mth
.
dex
()));
}
else
if
(
indexArg
.
isInsnWrap
())
{
InsnNode
constInsn
=
((
InsnWrapArg
)
indexArg
).
getWrapInsn
();
index
=
getIntConst
(
InsnUtils
.
getConstValueByInsn
(
mth
.
dex
(),
constInsn
));
}
return
index
==
putIndex
;
}
private
static
int
getIntConst
(
Object
value
)
{
if
(
value
instanceof
Integer
)
{
return
(
Integer
)
value
;
}
if
(
value
instanceof
Long
)
{
return
((
Long
)
value
).
intValue
();
}
return
filledArr
;
return
-
1
;
}
private
static
void
processEnumSwitch
(
MethodNode
mth
,
SwitchNode
insn
)
{
...
...
jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java
View file @
389caf18
...
...
@@ -44,6 +44,10 @@ public class InstructionRemover {
public
void
add
(
InsnNode
insn
)
{
toRemove
.
add
(
insn
);
}
public
void
addAndUnbind
(
MethodNode
mth
,
InsnNode
insn
)
{
toRemove
.
add
(
insn
);
unbindInsn
(
mth
,
insn
);
}
public
void
perform
()
{
if
(
toRemove
.
isEmpty
())
{
...
...
@@ -65,7 +69,7 @@ public class InstructionRemover {
}
}
unbindResult
(
mth
,
insn
);
insn
.
add
(
AFlag
.
INCONSISTENT_COD
E
);
insn
.
add
(
AFlag
.
REMOV
E
);
}
public
static
void
fixUsedInPhiFlag
(
RegisterArg
useReg
)
{
...
...
jadx-core/src/main/java/jadx/core/utils/Utils.java
View file @
389caf18
...
...
@@ -11,6 +11,8 @@ import java.util.List;
import
java.util.Map
;
import
java.util.function.Function
;
import
org.jetbrains.annotations.Nullable
;
import
jadx.api.JadxDecompiler
;
import
jadx.core.codegen.CodeWriter
;
...
...
@@ -174,4 +176,12 @@ public class Utils {
}
return
Collections
.
unmodifiableMap
(
result
);
}
@Nullable
public
static
<
T
>
T
last
(
List
<
T
>
list
)
{
if
(
list
.
isEmpty
())
{
return
null
;
}
return
list
.
get
(
list
.
size
()
-
1
);
}
}
jadx-core/src/test/java/jadx/tests/integration/arrays/TestMultiDimArrayFill.java
0 → 100644
View file @
389caf18
package
jadx
.
tests
.
integration
.
arrays
;
import
org.junit.Test
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.tests.api.IntegrationTest
;
import
static
org
.
hamcrest
.
CoreMatchers
.
containsString
;
import
static
org
.
junit
.
Assert
.
assertThat
;
public
class
TestMultiDimArrayFill
extends
IntegrationTest
{
public
static
class
TestCls
{
public
static
Obj
test
(
int
a
,
int
b
)
{
return
new
Obj
(
new
int
[][]{
new
int
[]{
1
},
new
int
[]{
2
},
{
3
},
new
int
[]{
4
,
5
},
new
int
[
0
]
},
new
int
[]{
a
,
a
,
a
,
a
,
b
}
);
}
private
static
class
Obj
{
public
Obj
(
int
[][]
ints
,
int
[]
ints2
)
{}
}
}
@Test
public
void
test
()
{
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"return new Obj("
+
"new int[][]{new int[]{1}, new int[]{2}, new int[]{3}, new int[]{4, 5}, new int[0]}, "
+
"new int[]{a, a, a, a, b});"
));
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment