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
3700ecb7
Commit
3700ecb7
authored
Jan 02, 2015
by
Skylot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
core: add resources methods to jadx API
parent
811b0e7f
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
525 additions
and
185 deletions
+525
-185
JadxCLIArgs.java
jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
+13
-5
DefaultJadxArgs.java
jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java
+6
-1
IJadxArgs.java
jadx-core/src/main/java/jadx/api/IJadxArgs.java
+3
-1
JadxDecompiler.java
jadx-core/src/main/java/jadx/api/JadxDecompiler.java
+63
-25
ResourceFile.java
jadx-core/src/main/java/jadx/api/ResourceFile.java
+63
-0
ResourceType.java
jadx-core/src/main/java/jadx/api/ResourceType.java
+50
-0
ResourcesLoader.java
jadx-core/src/main/java/jadx/api/ResourcesLoader.java
+143
-0
BinaryXMLParser.java
...-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java
+179
-153
IntegrationTest.java
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
+5
-0
No files found.
jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
View file @
3700ecb7
...
@@ -32,6 +32,12 @@ public final class JadxCLIArgs implements IJadxArgs {
...
@@ -32,6 +32,12 @@ public final class JadxCLIArgs implements IJadxArgs {
@Parameter
(
names
=
{
"-f"
,
"--fallback"
},
description
=
"make simple dump (using goto instead of 'if', 'for', etc)"
)
@Parameter
(
names
=
{
"-f"
,
"--fallback"
},
description
=
"make simple dump (using goto instead of 'if', 'for', etc)"
)
protected
boolean
fallbackMode
=
false
;
protected
boolean
fallbackMode
=
false
;
@Parameter
(
names
=
{
"-r"
,
"--no-res"
},
description
=
"do not decode resources"
)
protected
boolean
skipResources
=
false
;
@Parameter
(
names
=
{
"-s"
,
"--no-src"
},
description
=
"do not decompile source code"
)
protected
boolean
skipSources
=
false
;
@Parameter
(
names
=
{
"--show-bad-code"
},
description
=
"show inconsistent code (incorrectly decompiled)"
)
@Parameter
(
names
=
{
"--show-bad-code"
},
description
=
"show inconsistent code (incorrectly decompiled)"
)
protected
boolean
showInconsistentCode
=
false
;
protected
boolean
showInconsistentCode
=
false
;
...
@@ -47,9 +53,6 @@ public final class JadxCLIArgs implements IJadxArgs {
...
@@ -47,9 +53,6 @@ public final class JadxCLIArgs implements IJadxArgs {
@Parameter
(
names
=
{
"-h"
,
"--help"
},
description
=
"print this help"
,
help
=
true
)
@Parameter
(
names
=
{
"-h"
,
"--help"
},
description
=
"print this help"
,
help
=
true
)
protected
boolean
printHelp
=
false
;
protected
boolean
printHelp
=
false
;
@Parameter
(
names
=
{
"-x"
,
"--xml"
},
description
=
"try to decode the AndroidManifest.xml"
)
protected
boolean
xmlTest
=
false
;
private
final
List
<
File
>
input
=
new
ArrayList
<
File
>(
1
);
private
final
List
<
File
>
input
=
new
ArrayList
<
File
>(
1
);
private
File
outputDir
;
private
File
outputDir
;
...
@@ -168,8 +171,13 @@ public final class JadxCLIArgs implements IJadxArgs {
...
@@ -168,8 +171,13 @@ public final class JadxCLIArgs implements IJadxArgs {
}
}
@Override
@Override
public
boolean
isXMLTest
()
{
public
boolean
isSkipResources
()
{
return
xmlTest
;
return
skipResources
;
}
@Override
public
boolean
isSkipSources
()
{
return
skipSources
;
}
}
@Override
@Override
...
...
jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java
View file @
3700ecb7
...
@@ -40,7 +40,12 @@ public class DefaultJadxArgs implements IJadxArgs {
...
@@ -40,7 +40,12 @@ public class DefaultJadxArgs implements IJadxArgs {
}
}
@Override
@Override
public
boolean
isXMLTest
()
{
public
boolean
isSkipResources
()
{
return
false
;
}
@Override
public
boolean
isSkipSources
()
{
return
false
;
return
false
;
}
}
}
}
jadx-core/src/main/java/jadx/api/IJadxArgs.java
View file @
3700ecb7
...
@@ -17,5 +17,7 @@ public interface IJadxArgs {
...
@@ -17,5 +17,7 @@ public interface IJadxArgs {
boolean
isVerbose
();
boolean
isVerbose
();
boolean
isXMLTest
();
boolean
isSkipResources
();
boolean
isSkipSources
();
}
}
jadx-core/src/main/java/jadx/api/JadxDecompiler.java
View file @
3700ecb7
...
@@ -2,6 +2,7 @@ package jadx.api;
...
@@ -2,6 +2,7 @@ package jadx.api;
import
jadx.core.Jadx
;
import
jadx.core.Jadx
;
import
jadx.core.ProcessClass
;
import
jadx.core.ProcessClass
;
import
jadx.core.codegen.CodeWriter
;
import
jadx.core.dex.info.ClassInfo
;
import
jadx.core.dex.info.ClassInfo
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.RootNode
;
import
jadx.core.dex.nodes.RootNode
;
...
@@ -55,6 +56,9 @@ public final class JadxDecompiler {
...
@@ -55,6 +56,9 @@ public final class JadxDecompiler {
private
RootNode
root
;
private
RootNode
root
;
private
List
<
IDexTreeVisitor
>
passes
;
private
List
<
IDexTreeVisitor
>
passes
;
private
List
<
JavaClass
>
classes
;
private
List
<
JavaClass
>
classes
;
private
List
<
ResourceFile
>
resources
;
private
BinaryXMLParser
xmlParser
;
public
JadxDecompiler
()
{
public
JadxDecompiler
()
{
this
(
new
DefaultJadxArgs
());
this
(
new
DefaultJadxArgs
());
...
@@ -82,6 +86,8 @@ public final class JadxDecompiler {
...
@@ -82,6 +86,8 @@ public final class JadxDecompiler {
void
reset
()
{
void
reset
()
{
ClassInfo
.
clearCache
();
ClassInfo
.
clearCache
();
classes
=
null
;
classes
=
null
;
resources
=
null
;
xmlParser
=
null
;
root
=
null
;
root
=
null
;
}
}
...
@@ -108,27 +114,21 @@ public final class JadxDecompiler {
...
@@ -108,27 +114,21 @@ public final class JadxDecompiler {
parse
();
parse
();
}
}
public
void
parseAndSaveXML
()
{
public
void
save
()
{
if
(
this
.
args
.
isXMLTest
())
{
save
(!
args
.
isSkipSources
(),
!
args
.
isSkipResources
());
InputFile
inf
=
inputFiles
.
get
(
0
);
try
{
byte
[]
buffer
=
InputFile
.
loadXMLBuffer
(
inf
.
getFile
());
if
(
buffer
!=
null
)
{
File
out
=
new
File
(
outDir
,
"AndroidManifest.xml"
);
BinaryXMLParser
bxp
=
new
BinaryXMLParser
(
root
);
bxp
.
parse
(
buffer
,
out
);
}
}
catch
(
Exception
e
)
{
LOG
.
info
(
"Decompiling AndroidManifest.xml failed!"
,
e
);
}
}
}
}
public
void
save
()
{
public
void
saveSources
()
{
parseAndSaveXML
();
save
(
true
,
false
);
}
public
void
saveResources
()
{
save
(
false
,
true
);
}
private
void
save
(
boolean
saveSources
,
boolean
saveResources
)
{
try
{
try
{
ExecutorService
ex
=
getSaveExecutor
();
ExecutorService
ex
=
getSaveExecutor
(
saveSources
,
saveResources
);
ex
.
shutdown
();
ex
.
shutdown
();
ex
.
awaitTermination
(
1
,
TimeUnit
.
DAYS
);
ex
.
awaitTermination
(
1
,
TimeUnit
.
DAYS
);
}
catch
(
InterruptedException
e
)
{
}
catch
(
InterruptedException
e
)
{
...
@@ -137,6 +137,10 @@ public final class JadxDecompiler {
...
@@ -137,6 +137,10 @@ public final class JadxDecompiler {
}
}
public
ExecutorService
getSaveExecutor
()
{
public
ExecutorService
getSaveExecutor
()
{
return
getSaveExecutor
(!
args
.
isSkipSources
(),
!
args
.
isSkipResources
());
}
private
ExecutorService
getSaveExecutor
(
boolean
saveSources
,
boolean
saveResources
)
{
if
(
root
==
null
)
{
if
(
root
==
null
)
{
throw
new
JadxRuntimeException
(
"No loaded files"
);
throw
new
JadxRuntimeException
(
"No loaded files"
);
}
}
...
@@ -145,14 +149,31 @@ public final class JadxDecompiler {
...
@@ -145,14 +149,31 @@ public final class JadxDecompiler {
LOG
.
info
(
"processing ..."
);
LOG
.
info
(
"processing ..."
);
ExecutorService
executor
=
Executors
.
newFixedThreadPool
(
threadsCount
);
ExecutorService
executor
=
Executors
.
newFixedThreadPool
(
threadsCount
);
for
(
final
JavaClass
cls
:
getClasses
())
{
if
(
saveSources
)
{
executor
.
execute
(
new
Runnable
()
{
for
(
final
JavaClass
cls
:
getClasses
())
{
@Override
executor
.
execute
(
new
Runnable
()
{
public
void
run
()
{
@Override
cls
.
decompile
();
public
void
run
()
{
SaveCode
.
save
(
outDir
,
args
,
cls
.
getClassNode
());
cls
.
decompile
();
}
SaveCode
.
save
(
outDir
,
args
,
cls
.
getClassNode
());
});
}
});
}
}
if
(
saveResources
)
{
for
(
final
ResourceFile
resourceFile
:
getResources
())
{
executor
.
execute
(
new
Runnable
()
{
@Override
public
void
run
()
{
if
(
ResourceType
.
isSupportedForUnpack
(
resourceFile
.
getType
()))
{
CodeWriter
cw
=
resourceFile
.
getContent
();
if
(
cw
!=
null
)
{
cw
.
save
(
new
File
(
outDir
,
resourceFile
.
getName
()));
}
}
}
});
}
}
}
return
executor
;
return
executor
;
}
}
...
@@ -172,6 +193,16 @@ public final class JadxDecompiler {
...
@@ -172,6 +193,16 @@ public final class JadxDecompiler {
return
classes
;
return
classes
;
}
}
public
List
<
ResourceFile
>
getResources
()
{
if
(
resources
==
null
)
{
if
(
root
==
null
)
{
return
Collections
.
emptyList
();
}
resources
=
new
ResourcesLoader
(
this
).
load
(
inputFiles
);
}
return
resources
;
}
public
List
<
JavaPackage
>
getPackages
()
{
public
List
<
JavaPackage
>
getPackages
()
{
List
<
JavaClass
>
classList
=
getClasses
();
List
<
JavaClass
>
classList
=
getClasses
();
if
(
classList
.
isEmpty
())
{
if
(
classList
.
isEmpty
())
{
...
@@ -232,6 +263,13 @@ public final class JadxDecompiler {
...
@@ -232,6 +263,13 @@ public final class JadxDecompiler {
return
root
;
return
root
;
}
}
BinaryXMLParser
getXmlParser
()
{
if
(
xmlParser
==
null
)
{
xmlParser
=
new
BinaryXMLParser
(
root
);
}
return
xmlParser
;
}
JavaClass
findJavaClass
(
ClassNode
cls
)
{
JavaClass
findJavaClass
(
ClassNode
cls
)
{
if
(
cls
==
null
)
{
if
(
cls
==
null
)
{
return
null
;
return
null
;
...
...
jadx-core/src/main/java/jadx/api/ResourceFile.java
0 → 100644
View file @
3700ecb7
package
jadx
.
api
;
import
jadx.core.codegen.CodeWriter
;
import
java.io.File
;
public
class
ResourceFile
{
public
static
final
class
ZipRef
{
private
final
File
zipFile
;
private
final
String
entryName
;
public
ZipRef
(
File
zipFile
,
String
entryName
)
{
this
.
zipFile
=
zipFile
;
this
.
entryName
=
entryName
;
}
public
File
getZipFile
()
{
return
zipFile
;
}
public
String
getEntryName
()
{
return
entryName
;
}
@Override
public
String
toString
()
{
return
"ZipRef{"
+
zipFile
+
", '"
+
entryName
+
"'}"
;
}
}
private
final
JadxDecompiler
decompiler
;
private
final
String
name
;
private
final
ResourceType
type
;
private
ZipRef
zipRef
;
ResourceFile
(
JadxDecompiler
decompiler
,
String
name
,
ResourceType
type
)
{
this
.
decompiler
=
decompiler
;
this
.
name
=
name
;
this
.
type
=
type
;
}
public
String
getName
()
{
return
name
;
}
public
ResourceType
getType
()
{
return
type
;
}
public
CodeWriter
getContent
()
{
return
ResourcesLoader
.
loadContent
(
decompiler
,
zipRef
,
type
);
}
void
setZipRef
(
ZipRef
zipRef
)
{
this
.
zipRef
=
zipRef
;
}
@Override
public
String
toString
()
{
return
"ResourceFile{name='"
+
name
+
'\''
+
", type="
+
type
+
"}"
;
}
}
jadx-core/src/main/java/jadx/api/ResourceType.java
0 → 100644
View file @
3700ecb7
package
jadx
.
api
;
public
enum
ResourceType
{
CODE
(
".dex"
,
".class"
),
MANIFEST
(
"AndroidManifest.xml"
),
XML
(
".xml"
),
// TODO binary or not?
ARSC
(
".arsc"
),
// TODO decompile !!!
FONT
(
".ttf"
),
IMG
(
".png"
,
".gif"
,
".jpg"
),
LIB
(
".so"
),
UNKNOWN
;
private
String
[]
exts
;
ResourceType
(
String
...
exts
)
{
this
.
exts
=
exts
;
}
public
String
[]
getExts
()
{
return
exts
;
}
public
static
ResourceType
getFileType
(
String
fileName
)
{
for
(
ResourceType
type
:
ResourceType
.
values
())
{
for
(
String
ext
:
type
.
getExts
())
{
if
(
fileName
.
endsWith
(
ext
))
{
return
type
;
}
}
}
return
UNKNOWN
;
}
public
static
boolean
isSupportedForUnpack
(
ResourceType
type
)
{
switch
(
type
)
{
case
CODE:
case
ARSC:
case
LIB:
case
XML:
case
FONT:
case
IMG:
case
UNKNOWN:
return
false
;
case
MANIFEST:
return
true
;
}
return
false
;
}
}
jadx-core/src/main/java/jadx/api/ResourcesLoader.java
0 → 100644
View file @
3700ecb7
package
jadx
.
api
;
import
jadx.api.ResourceFile.ZipRef
;
import
jadx.core.codegen.CodeWriter
;
import
jadx.core.utils.files.InputFile
;
import
java.io.BufferedInputStream
;
import
java.io.ByteArrayOutputStream
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.util.ArrayList
;
import
java.util.Enumeration
;
import
java.util.List
;
import
java.util.zip.ZipEntry
;
import
java.util.zip.ZipFile
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
// TODO: move to core package
final
class
ResourcesLoader
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
ResourcesLoader
.
class
);
private
static
final
int
READ_BUFFER_SIZE
=
8
*
1024
;
private
static
final
int
LOAD_SIZE_LIMIT
=
500
*
1024
;
private
JadxDecompiler
jadxRef
;
ResourcesLoader
(
JadxDecompiler
jadxRef
)
{
this
.
jadxRef
=
jadxRef
;
}
List
<
ResourceFile
>
load
(
List
<
InputFile
>
inputFiles
)
{
List
<
ResourceFile
>
list
=
new
ArrayList
<
ResourceFile
>(
inputFiles
.
size
());
for
(
InputFile
file
:
inputFiles
)
{
loadFile
(
list
,
file
.
getFile
());
}
return
list
;
}
static
CodeWriter
loadContent
(
JadxDecompiler
jadxRef
,
ZipRef
zipRef
,
ResourceType
type
)
{
if
(
zipRef
==
null
)
{
return
null
;
}
ZipFile
zipFile
=
null
;
InputStream
inputStream
=
null
;
try
{
zipFile
=
new
ZipFile
(
zipRef
.
getZipFile
());
ZipEntry
entry
=
zipFile
.
getEntry
(
zipRef
.
getEntryName
());
if
(
entry
!=
null
)
{
if
(
entry
.
getSize
()
>
LOAD_SIZE_LIMIT
)
{
return
new
CodeWriter
().
add
(
"File too big, size: "
+
String
.
format
(
"%.2f KB"
,
entry
.
getSize
()
/
1024
.));
}
inputStream
=
new
BufferedInputStream
(
zipFile
.
getInputStream
(
entry
));
return
decode
(
jadxRef
,
type
,
inputStream
);
}
else
{
LOG
.
warn
(
"Zip entry not found: {}"
,
zipRef
);
}
}
catch
(
IOException
e
)
{
LOG
.
error
(
"Error load: "
+
zipRef
,
e
);
}
finally
{
try
{
if
(
zipFile
!=
null
)
{
zipFile
.
close
();
}
if
(
inputStream
!=
null
)
{
inputStream
.
close
();
}
}
catch
(
Exception
e
)
{
LOG
.
debug
(
"Error close zip file: "
+
zipRef
,
e
);
}
}
return
null
;
}
private
static
CodeWriter
decode
(
JadxDecompiler
jadxRef
,
ResourceType
type
,
InputStream
inputStream
)
throws
IOException
{
switch
(
type
)
{
case
MANIFEST:
case
XML:
return
jadxRef
.
getXmlParser
().
parse
(
inputStream
);
}
return
loadToCodeWriter
(
inputStream
);
}
private
void
loadFile
(
List
<
ResourceFile
>
list
,
File
file
)
{
if
(
file
==
null
)
{
return
;
}
ZipFile
zip
=
null
;
try
{
zip
=
new
ZipFile
(
file
);
Enumeration
<?
extends
ZipEntry
>
entries
=
zip
.
entries
();
while
(
entries
.
hasMoreElements
())
{
ZipEntry
entry
=
entries
.
nextElement
();
addEntry
(
list
,
file
,
entry
);
}
}
catch
(
IOException
e
)
{
LOG
.
debug
(
"Not a zip file: "
+
file
.
getAbsolutePath
());
}
finally
{
if
(
zip
!=
null
)
{
try
{
zip
.
close
();
}
catch
(
Exception
e
)
{
LOG
.
error
(
"Zip file close error: "
+
file
.
getAbsolutePath
(),
e
);
}
}
}
}
private
void
addEntry
(
List
<
ResourceFile
>
list
,
File
zipFile
,
ZipEntry
entry
)
{
if
(
entry
.
isDirectory
())
{
return
;
}
String
name
=
entry
.
getName
();
ResourceType
type
=
ResourceType
.
getFileType
(
name
);
ResourceFile
rf
=
new
ResourceFile
(
jadxRef
,
name
,
type
);
rf
.
setZipRef
(
new
ZipRef
(
zipFile
,
name
));
list
.
add
(
rf
);
// LOG.debug("Add resource entry: {}, size: {}", name, entry.getSize());
}
private
static
CodeWriter
loadToCodeWriter
(
InputStream
is
)
throws
IOException
{
CodeWriter
cw
=
new
CodeWriter
();
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
(
READ_BUFFER_SIZE
);
byte
[]
buffer
=
new
byte
[
READ_BUFFER_SIZE
];
int
count
;
try
{
while
((
count
=
is
.
read
(
buffer
))
!=
-
1
)
{
baos
.
write
(
buffer
,
0
,
count
);
}
}
finally
{
try
{
is
.
close
();
}
catch
(
Exception
ignore
)
{
}
}
cw
.
add
(
baos
.
toString
(
"UTF-8"
));
return
cw
;
}
}
jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java
View file @
3700ecb7
...
@@ -5,11 +5,12 @@ import jadx.core.dex.instructions.args.ArgType;
...
@@ -5,11 +5,12 @@ import jadx.core.dex.instructions.args.ArgType;
import
jadx.core.dex.nodes.DexNode
;
import
jadx.core.dex.nodes.DexNode
;
import
jadx.core.dex.nodes.FieldNode
;
import
jadx.core.dex.nodes.FieldNode
;
import
jadx.core.dex.nodes.RootNode
;
import
jadx.core.dex.nodes.RootNode
;
import
jadx.core.utils.Utils
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Field
;
import
java.nio.ByteBuffer
;
import
java.nio.charset.Charset
;
import
java.nio.charset.Charset
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -32,9 +33,11 @@ public class BinaryXMLParser {
...
@@ -32,9 +33,11 @@ public class BinaryXMLParser {
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
BinaryXMLParser
.
class
);
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
BinaryXMLParser
.
class
);
private
byte
[]
bytes
;
private
static
final
Charset
STRING_CHARSET
=
Charset
.
forName
(
"UTF-16LE"
);
private
CodeWriter
writer
;
private
InputStream
input
;
private
String
[]
strings
;
private
String
[]
strings
;
private
int
count
;
private
String
nsPrefix
=
"ERROR"
;
private
String
nsPrefix
=
"ERROR"
;
private
String
nsURI
=
"ERROR"
;
private
String
nsURI
=
"ERROR"
;
...
@@ -43,9 +46,9 @@ public class BinaryXMLParser {
...
@@ -43,9 +46,9 @@ public class BinaryXMLParser {
private
boolean
firstElement
;
private
boolean
firstElement
;
private
boolean
wasOneLiner
=
false
;
private
boolean
wasOneLiner
=
false
;
private
CodeWriter
writer
;
private
Map
<
Integer
,
String
>
styleMap
=
new
HashMap
<
Integer
,
String
>();
private
Map
<
Integer
,
String
>
styleMap
=
new
HashMap
<
Integer
,
String
>();
private
Map
<
Integer
,
FieldNode
>
localStyleMap
=
new
HashMap
<
Integer
,
FieldNode
>();
private
Map
<
Integer
,
FieldNode
>
localStyleMap
=
new
HashMap
<
Integer
,
FieldNode
>();
private
final
ManifestAttributes
attributes
;
private
final
ManifestAttributes
attributes
;
public
BinaryXMLParser
(
RootNode
root
)
{
public
BinaryXMLParser
(
RootNode
root
)
{
...
@@ -70,30 +73,34 @@ public class BinaryXMLParser {
...
@@ -70,30 +73,34 @@ public class BinaryXMLParser {
}
}
}
}
public
void
parse
(
byte
[]
xmlBytes
,
File
out
)
{
public
synchronized
CodeWriter
parse
(
InputStream
inputStream
)
{
LOG
.
debug
(
"Decoding AndroidManifest.xml, output: {}"
,
out
);
writer
=
new
CodeWriter
();
writer
=
new
CodeWriter
();
writer
.
add
(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
);
writer
.
add
(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
);
bytes
=
xmlBytes
;
input
=
inputStream
;
count
=
0
;
firstElement
=
true
;
firstElement
=
true
;
decode
();
try
{
writer
.
save
(
out
);
decode
();
}
catch
(
IOException
e
)
{
LOG
.
debug
(
"Binary xml decode failed"
,
e
);
CodeWriter
cw
=
new
CodeWriter
();
cw
.
add
(
"Error decode binary xml"
);
cw
.
startLine
(
Utils
.
getStackTrace
(
e
));
return
cw
;
}
writer
.
finish
();
return
writer
;
}
}
void
decode
()
{
void
decode
()
throws
IOException
{
if
(
cInt16
(
bytes
,
count
)
!=
0x0003
)
{
if
(
cInt16
()
!=
0x0003
)
{
die
(
"Version is not 3"
);
die
(
"Version is not 3"
);
}
}
if
(
cInt16
(
bytes
,
count
)
!=
0x0008
)
{
if
(
cInt16
()
!=
0x0008
)
{
die
(
"Size of header is not 8"
);
die
(
"Size of header is not 8"
);
}
}
if
(
cInt32
(
bytes
,
count
)
!=
bytes
.
length
)
{
cInt32
();
die
(
"Size of manifest doesn't match"
);
while
(
input
.
available
()
!=
0
)
{
}
int
type
=
cInt16
();
while
((
count
+
2
)
<=
bytes
.
length
)
{
int
type
=
cInt16
(
bytes
,
count
);
switch
(
type
)
{
switch
(
type
)
{
case
0x0001
:
case
0x0001
:
parseStringPool
();
parseStringPool
();
...
@@ -124,105 +131,106 @@ public class BinaryXMLParser {
...
@@ -124,105 +131,106 @@ public class BinaryXMLParser {
}
}
}
}
private
void
parseStringPool
()
{
private
void
parseStringPool
()
throws
IOException
{
if
(
cInt16
(
bytes
,
count
)
!=
0x001c
)
{
if
(
cInt16
()
!=
0x001c
)
{
die
(
"Header header size not 28"
);
die
(
"Header header size not 28"
);
}
}
int
hsize
=
cInt32
(
bytes
,
count
);
int
hsize
=
cInt32
();
int
stringCount
=
cInt32
(
bytes
,
count
);
int
stringCount
=
cInt32
();
int
styleCount
=
cInt32
(
bytes
,
count
);
int
styleCount
=
cInt32
();
int
flags
=
cInt32
(
bytes
,
count
);
int
flags
=
cInt32
();
int
stringsStart
=
cInt32
(
bytes
,
count
);
int
stringsStart
=
cInt32
();
int
stylesStart
=
cInt32
(
bytes
,
count
);
int
stylesStart
=
cInt32
();
int
[]
stringsOffsets
=
new
int
[
stringCount
];
int
[]
stringsOffsets
=
new
int
[
stringCount
];
for
(
int
i
=
0
;
i
<
stringCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
stringCount
;
i
++)
{
stringsOffsets
[
i
]
=
cInt32
(
bytes
,
count
);
stringsOffsets
[
i
]
=
cInt32
();
}
}
strings
=
new
String
[
stringCount
];
strings
=
new
String
[
stringCount
];
for
(
int
i
=
0
;
i
<
stringCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
stringCount
;
i
++)
{
int
off
=
8
+
stringsStart
+
stringsOffsets
[
i
];
int
off
=
8
+
stringsStart
+
stringsOffsets
[
i
];
int
strlen
=
cInt16
(
bytes
,
off
);
int
strlen
=
cInt16
();
byte
[]
str
=
new
byte
[
strlen
*
2
];
byte
[]
str
=
new
byte
[
strlen
*
2
];
System
.
arraycopy
(
bytes
,
count
,
str
,
0
,
strlen
*
2
);
readToArray
(
str
);
count
+=
strlen
*
2
;
strings
[
i
]
=
new
String
(
str
,
STRING_CHARSET
);
strings
[
i
]
=
new
String
(
str
,
Charset
.
forName
(
"UTF-16LE"
));
cInt16
();
count
+=
2
;
}
}
}
}
private
void
parseResourceMap
()
{
private
void
parseResourceMap
()
throws
IOException
{
if
(
cInt16
(
bytes
,
count
)
!=
0x8
)
{
if
(
cInt16
()
!=
0x8
)
{
die
(
"Header size of resmap is not 8!"
);
die
(
"Header size of resmap is not 8!"
);
}
}
int
rhsize
=
cInt32
(
bytes
,
count
);
int
rhsize
=
cInt32
();
int
[]
ids
=
new
int
[(
rhsize
-
8
)
/
4
];
int
[]
ids
=
new
int
[(
rhsize
-
8
)
/
4
];
for
(
int
i
=
0
;
i
<
ids
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
ids
.
length
;
i
++)
{
ids
[
i
]
=
cInt32
(
bytes
,
count
);
ids
[
i
]
=
cInt32
();
}
}
}
}
private
void
parseNameSpace
()
{
private
void
parseNameSpace
()
throws
IOException
{
if
(
cInt16
(
bytes
,
count
)
!=
0x00
10
)
{
if
(
cInt16
(
)
!=
0x
10
)
{
die
(
"NAMESPACE header is not 0x0010"
);
die
(
"NAMESPACE header is not 0x0010"
);
}
}
if
(
cInt32
(
bytes
,
count
)
!=
0x18
)
{
if
(
cInt32
()
!=
0x18
)
{
die
(
"NAMESPACE header chunk is not 0x18 big"
);
die
(
"NAMESPACE header chunk is not 0x18 big"
);
}
}
int
beginLineNumber
=
cInt32
(
bytes
,
count
);
int
beginLineNumber
=
cInt32
();
int
comment
=
cInt32
(
bytes
,
count
);
int
comment
=
cInt32
();
int
beginPrefix
=
cInt32
(
bytes
,
count
);
int
beginPrefix
=
cInt32
();
nsPrefix
=
strings
[
beginPrefix
];
nsPrefix
=
strings
[
beginPrefix
];
int
beginURI
=
cInt32
(
bytes
,
count
);
int
beginURI
=
cInt32
();
nsURI
=
strings
[
beginURI
];
nsURI
=
strings
[
beginURI
];
}
}
private
void
parseNameSpaceEnd
()
{
private
void
parseNameSpaceEnd
()
throws
IOException
{
if
(
cInt16
(
bytes
,
count
)
!=
0x00
10
)
{
if
(
cInt16
(
)
!=
0x
10
)
{
die
(
"NAMESPACE header is not 0x0010"
);
die
(
"NAMESPACE header is not 0x0010"
);
}
}
if
(
cInt32
(
bytes
,
count
)
!=
0x18
)
{
if
(
cInt32
()
!=
0x18
)
{
die
(
"NAMESPACE header chunk is not 0x18 big"
);
die
(
"NAMESPACE header chunk is not 0x18 big"
);
}
}
int
endLineNumber
=
cInt32
(
bytes
,
count
);
int
endLineNumber
=
cInt32
();
int
comment
=
cInt32
(
bytes
,
count
);
int
comment
=
cInt32
();
int
endPrefix
=
cInt32
(
bytes
,
count
);
int
endPrefix
=
cInt32
();
nsPrefix
=
strings
[
endPrefix
];
nsPrefix
=
strings
[
endPrefix
];
int
endURI
=
cInt32
(
bytes
,
count
);
int
endURI
=
cInt32
();
nsURI
=
strings
[
endURI
];
nsURI
=
strings
[
endURI
];
}
}
private
void
parseElement
()
{
private
void
parseElement
()
throws
IOException
{
if
(
firstElement
)
{
if
(
firstElement
)
{
firstElement
=
false
;
firstElement
=
false
;
}
else
{
}
else
{
writer
.
incIndent
();
writer
.
incIndent
();
}
}
if
(
cInt16
(
bytes
,
count
)
!=
0x00
10
)
{
if
(
cInt16
(
)
!=
0x
10
)
{
die
(
"ELEMENT HEADER SIZE is not 0x10"
);
die
(
"ELEMENT HEADER SIZE is not 0x10"
);
}
}
count
+=
4
;
// TODO: Check element chunk size
// TODO: Check element chunk size
int
elementBegLineNumber
=
cInt32
(
bytes
,
count
);
cInt32
();
int
comment
=
cInt32
(
bytes
,
count
);
int
elementBegLineNumber
=
cInt32
();
int
startNS
=
cInt32
(
bytes
,
count
);
int
comment
=
cInt32
();
int
startNSName
=
cInt32
(
bytes
,
count
);
// actually is elementName...
int
startNS
=
cInt32
();
int
startNSName
=
cInt32
();
// actually is elementName...
if
(!
wasOneLiner
&&
!
"ERROR"
.
equals
(
currentTag
)
&&
!
currentTag
.
equals
(
strings
[
startNSName
]))
{
if
(!
wasOneLiner
&&
!
"ERROR"
.
equals
(
currentTag
)
&&
!
currentTag
.
equals
(
strings
[
startNSName
]))
{
writer
.
add
(
">"
);
writer
.
add
(
">"
);
}
}
wasOneLiner
=
false
;
wasOneLiner
=
false
;
currentTag
=
strings
[
startNSName
];
currentTag
=
strings
[
startNSName
];
writer
.
startLine
(
"<"
).
add
(
strings
[
startNSName
]);
writer
.
startLine
(
"<"
).
add
(
strings
[
startNSName
]);
int
attributeStart
=
cInt16
(
bytes
,
count
);
writer
.
attachSourceLine
(
elementBegLineNumber
);
int
attributeStart
=
cInt16
();
if
(
attributeStart
!=
0x14
)
{
if
(
attributeStart
!=
0x14
)
{
die
(
"startNS's attributeStart is not 0x14"
);
die
(
"startNS's attributeStart is not 0x14"
);
}
}
int
attributeSize
=
cInt16
(
bytes
,
count
);
int
attributeSize
=
cInt16
();
if
(
attributeSize
!=
0x14
)
{
if
(
attributeSize
!=
0x14
)
{
die
(
"startNS's attributeSize is not 0x14"
);
die
(
"startNS's attributeSize is not 0x14"
);
}
}
int
attributeCount
=
cInt16
(
bytes
,
count
);
int
attributeCount
=
cInt16
();
int
idIndex
=
cInt16
(
bytes
,
count
);
int
idIndex
=
cInt16
();
int
classIndex
=
cInt16
(
bytes
,
count
);
int
classIndex
=
cInt16
();
int
styleIndex
=
cInt16
(
bytes
,
count
);
int
styleIndex
=
cInt16
();
if
(
"manifest"
.
equals
(
strings
[
startNSName
]))
{
if
(
"manifest"
.
equals
(
strings
[
startNSName
]))
{
writer
.
add
(
" xmlns:\""
).
add
(
nsURI
).
add
(
"\""
);
writer
.
add
(
" xmlns:\""
).
add
(
nsURI
).
add
(
"\""
);
}
}
...
@@ -230,71 +238,7 @@ public class BinaryXMLParser {
...
@@ -230,71 +238,7 @@ public class BinaryXMLParser {
writer
.
add
(
" "
);
writer
.
add
(
" "
);
}
}
for
(
int
i
=
0
;
i
<
attributeCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
attributeCount
;
i
++)
{
int
attributeNS
=
cInt32
(
bytes
,
count
);
parseAttribute
(
i
);
int
attributeName
=
cInt32
(
bytes
,
count
);
int
attributeRawValue
=
cInt32
(
bytes
,
count
);
int
attrValSize
=
cInt16
(
bytes
,
count
);
if
(
attrValSize
!=
0x08
)
{
die
(
"attrValSize != 0x08 not supported"
);
}
if
(
cInt8
(
bytes
,
count
)
!=
0
)
{
die
(
"res0 is not 0"
);
}
int
attrValDataType
=
cInt8
(
bytes
,
count
);
int
attrValData
=
cInt32
(
bytes
,
count
);
if
(
attributeNS
!=
-
1
)
{
writer
.
add
(
nsPrefix
).
add
(
':'
);
}
String
attrName
=
strings
[
attributeName
];
writer
.
add
(
attrName
).
add
(
"=\""
);
String
decodedAttr
=
attributes
.
decode
(
attrName
,
attrValData
);
if
(
decodedAttr
!=
null
)
{
writer
.
add
(
decodedAttr
);
}
else
{
switch
(
attrValDataType
)
{
case
0x3
:
writer
.
add
(
strings
[
attrValData
]);
break
;
case
0x10
:
writer
.
add
(
String
.
valueOf
(
attrValData
));
break
;
case
0x12
:
// FIXME: What to do, when data is always -1?
if
(
attrValData
==
0
)
{
writer
.
add
(
"false"
);
}
else
if
(
attrValData
==
1
||
attrValData
==
-
1
)
{
writer
.
add
(
"true"
);
}
else
{
writer
.
add
(
"UNKNOWN_BOOLEAN_TYPE"
);
}
break
;
case
0x1
:
String
name
=
styleMap
.
get
(
attrValData
);
if
(
name
!=
null
)
{
writer
.
add
(
"@*"
);
if
(
attributeNS
!=
-
1
)
{
writer
.
add
(
nsPrefix
).
add
(
':'
);
}
writer
.
add
(
"style/"
).
add
(
name
.
replaceAll
(
"_"
,
"."
));
}
else
{
FieldNode
field
=
localStyleMap
.
get
(
attrValData
);
if
(
field
!=
null
)
{
String
cls
=
field
.
getParentClass
().
getShortName
().
toLowerCase
();
writer
.
add
(
"@"
).
add
(
cls
).
add
(
"/"
).
add
(
field
.
getName
());
}
else
{
writer
.
add
(
"0x"
).
add
(
Integer
.
toHexString
(
attrValData
));
}
}
break
;
default
:
writer
.
add
(
"UNKNOWN_DATA_TYPE_"
+
attrValDataType
);
break
;
}
}
writer
.
add
(
'"'
);
writer
.
add
(
'"'
);
if
((
i
+
1
)
<
attributeCount
)
{
if
((
i
+
1
)
<
attributeCount
)
{
writer
.
add
(
" "
);
writer
.
add
(
" "
);
...
@@ -302,22 +246,95 @@ public class BinaryXMLParser {
...
@@ -302,22 +246,95 @@ public class BinaryXMLParser {
}
}
}
}
private
void
parseElementEnd
()
{
private
void
parseAttribute
(
int
i
)
throws
IOException
{
if
(
cInt16
(
bytes
,
count
)
!=
0x0010
)
{
int
attributeNS
=
cInt32
();
die
(
"ELEMENT END header is not 0x0010"
);
int
attributeName
=
cInt32
();
int
attributeRawValue
=
cInt32
();
int
attrValSize
=
cInt16
();
if
(
attrValSize
!=
0x08
)
{
die
(
"attrValSize != 0x08 not supported"
);
}
if
(
cInt8
()
!=
0
)
{
die
(
"res0 is not 0"
);
}
int
attrValDataType
=
cInt8
();
int
attrValData
=
cInt32
();
if
(
attributeNS
!=
-
1
)
{
writer
.
add
(
nsPrefix
).
add
(
':'
);
}
String
attrName
=
strings
[
attributeName
];
writer
.
add
(
attrName
).
add
(
"=\""
);
String
decodedAttr
=
attributes
.
decode
(
attrName
,
attrValData
);
if
(
decodedAttr
!=
null
)
{
writer
.
add
(
decodedAttr
);
}
else
{
decodeAttribute
(
attributeNS
,
attrValDataType
,
attrValData
);
}
}
private
void
decodeAttribute
(
int
attributeNS
,
int
attrValDataType
,
int
attrValData
)
{
switch
(
attrValDataType
)
{
case
0x3
:
writer
.
add
(
strings
[
attrValData
]);
break
;
case
0x10
:
writer
.
add
(
String
.
valueOf
(
attrValData
));
break
;
case
0x12
:
// FIXME: What to do, when data is always -1?
if
(
attrValData
==
0
)
{
writer
.
add
(
"false"
);
}
else
if
(
attrValData
==
1
||
attrValData
==
-
1
)
{
writer
.
add
(
"true"
);
}
else
{
writer
.
add
(
"UNKNOWN_BOOLEAN_TYPE"
);
}
break
;
case
0x1
:
String
name
=
styleMap
.
get
(
attrValData
);
if
(
name
!=
null
)
{
writer
.
add
(
"@*"
);
if
(
attributeNS
!=
-
1
)
{
writer
.
add
(
nsPrefix
).
add
(
':'
);
}
writer
.
add
(
"style/"
).
add
(
name
.
replaceAll
(
"_"
,
"."
));
}
else
{
FieldNode
field
=
localStyleMap
.
get
(
attrValData
);
if
(
field
!=
null
)
{
String
cls
=
field
.
getParentClass
().
getShortName
().
toLowerCase
();
writer
.
add
(
"@"
).
add
(
cls
).
add
(
"/"
).
add
(
field
.
getName
());
}
else
{
writer
.
add
(
"0x"
).
add
(
Integer
.
toHexString
(
attrValData
));
}
}
break
;
default
:
writer
.
add
(
"UNKNOWN_DATA_TYPE_"
+
attrValDataType
);
break
;
}
}
private
void
parseElementEnd
()
throws
IOException
{
if
(
cInt16
()
!=
0x10
)
{
die
(
"ELEMENT END header is not 0x10"
);
}
}
if
(
cInt32
(
bytes
,
count
)
!=
0x18
)
{
if
(
cInt32
()
!=
0x18
)
{
die
(
"ELEMENT END header chunk is not 0x18 big"
);
die
(
"ELEMENT END header chunk is not 0x18 big"
);
}
}
int
endLineNumber
=
cInt32
(
bytes
,
count
);
int
endLineNumber
=
cInt32
();
int
comment
=
cInt32
(
bytes
,
count
);
int
comment
=
cInt32
();
int
elementNS
=
cInt32
(
bytes
,
count
);
int
elementNS
=
cInt32
();
int
elementName
=
cInt32
(
bytes
,
count
);
int
elementName
=
cInt32
();
if
(
currentTag
.
equals
(
strings
[
elementName
]))
{
if
(
currentTag
.
equals
(
strings
[
elementName
]))
{
writer
.
add
(
" />"
);
writer
.
add
(
" />"
);
wasOneLiner
=
true
;
wasOneLiner
=
true
;
}
else
{
}
else
{
writer
.
startLine
(
"</"
);
writer
.
startLine
(
"</"
);
writer
.
attachSourceLine
(
endLineNumber
);
if
(
elementNS
!=
-
1
)
{
if
(
elementNS
!=
-
1
)
{
writer
.
add
(
strings
[
elementNS
]).
add
(
':'
);
writer
.
add
(
strings
[
elementNS
]).
add
(
':'
);
}
}
...
@@ -328,26 +345,35 @@ public class BinaryXMLParser {
...
@@ -328,26 +345,35 @@ public class BinaryXMLParser {
}
}
}
}
private
int
cInt8
(
byte
[]
bytes
,
int
offset
)
{
private
int
cInt8
()
throws
IOException
{
byte
[]
tmp
=
new
byte
[
4
];
return
input
.
read
();
tmp
[
3
]
=
bytes
[
count
++];
}
return
ByteBuffer
.
wrap
(
tmp
).
getInt
();
private
int
cInt16
()
throws
IOException
{
int
b1
=
input
.
read
();
int
b2
=
input
.
read
();
return
(
b2
&
0xFF
)
<<
8
|
(
b1
&
0xFF
);
}
}
private
int
cInt16
(
byte
[]
bytes
,
int
offset
)
{
private
int
cInt32
()
throws
IOException
{
byte
[]
tmp
=
new
byte
[
4
];
InputStream
in
=
input
;
tmp
[
3
]
=
bytes
[
count
++];
int
b1
=
in
.
read
();
tmp
[
2
]
=
bytes
[
count
++];
int
b2
=
in
.
read
();
return
ByteBuffer
.
wrap
(
tmp
).
getInt
();
int
b3
=
in
.
read
();
int
b4
=
in
.
read
();
return
b4
<<
24
|
(
b3
&
0xFF
)
<<
16
|
(
b2
&
0xFF
)
<<
8
|
(
b1
&
0xFF
);
}
}
private
int
cInt32
(
byte
[]
bytes
,
int
offset
)
{
private
void
readToArray
(
byte
[]
arr
)
throws
IOException
{
byte
[]
tmp
=
new
byte
[
4
];
int
count
=
arr
.
length
;
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
int
pos
=
input
.
read
(
arr
,
0
,
count
);
tmp
[
3
-
i
]
=
bytes
[
count
+
i
];
while
(
pos
<
count
)
{
int
read
=
input
.
read
(
arr
,
pos
,
count
-
pos
);
if
(
read
==
-
1
)
{
throw
new
IOException
(
"No data, can't read "
+
count
+
" bytes"
);
}
pos
+=
read
;
}
}
count
+=
4
;
return
ByteBuffer
.
wrap
(
tmp
).
getInt
();
}
}
private
void
die
(
String
message
)
{
private
void
die
(
String
message
)
{
...
...
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
View file @
3700ecb7
...
@@ -128,6 +128,11 @@ public abstract class IntegrationTest extends TestUtils {
...
@@ -128,6 +128,11 @@ public abstract class IntegrationTest extends TestUtils {
public
int
getThreadsCount
()
{
public
int
getThreadsCount
()
{
return
1
;
return
1
;
}
}
@Override
public
boolean
isSkipResources
()
{
return
true
;
}
},
new
File
(
outDir
));
},
new
File
(
outDir
));
}
}
...
...
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