-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathninjadroid.py
executable file
·148 lines (126 loc) · 4.49 KB
/
ninjadroid.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python3
"""
Ninja Reverse Engineering of Android APK packages.
:author: Paolo Rovelli
:copyright: GNU General Public License v3.0 (https://www.gnu.org/licenses/gpl.html).
"""
from argparse import ArgumentParser, Namespace, RawTextHelpFormatter
import logging
import os
import re
import sys
from typing import Optional
from ninjadroid.use_cases.extract_certificate_file import ExtractCertificateFile
from ninjadroid.use_cases.extract_dex_file import ExtractDexFile
from ninjadroid.use_cases.generate_apk_info_report import GenerateApkInfoReport
from ninjadroid.use_cases.launch_apk_tool import LaunchApkTool
from ninjadroid.use_cases.launch_dex2jar import LaunchDex2Jar
from ninjadroid.use_cases.print_apk_info import PrintApkInfo
from ninjadroid.parsers.file import FileParsingError
from ninjadroid.parsers.apk import APK, ApkParser, ApkParsingError
VERSION = "4.5"
logging.basicConfig(
format=" >> %(name)s: [%(levelname)s] %(message)s",
level=logging.INFO
)
logger = logging.getLogger("NinjaDroid")
def main():
args = get_args()
if args.verbose:
logger.setLevel(logging.DEBUG)
apk = read_file(args.target, args.extended_processing)
if apk is None:
return 1
filename = get_filename_without_extension(args.target)
if args.output_directory is None:
PrintApkInfo().execute(apk, as_json=args.json)
else:
output_directory = setup_output_directory(args.output_directory, filename)
LaunchApkTool(logger).execute(args.target, output_directory)
LaunchDex2Jar(logger).execute(args.target, filename, output_directory)
ExtractCertificateFile(logger).execute(apk, output_directory)
ExtractDexFile(logger).execute(apk, output_directory)
GenerateApkInfoReport(logger).execute(apk, filename, output_directory)
return 0
def get_args() -> Namespace:
parser = ArgumentParser(
description="examples: \n"
" >> %(prog)s /path/to/file.apk\n"
" >> %(prog)s /path/to/file.apk --all\n"
" >> %(prog)s /path/to/file.apk --all --json\n"
" >> %(prog)s /path/to/file.apk --all --extract\n"
" >> %(prog)s /path/to/file.apk --all --extract /path/to/output/directory/\n",
formatter_class=RawTextHelpFormatter
)
parser.add_argument(
"target",
metavar="TARGET_FILE",
type=str,
help="the APK package to analyse"
)
parser.add_argument(
"-a",
"--all",
action="store_true",
dest="extended_processing",
help="retrieve and show all the information, only a summary otherwise"
)
parser.add_argument(
"-j",
"--json",
action="store_true",
dest="json",
help="show the output in JSON format"
)
parser.add_argument(
"-e",
"--extract",
type=str,
nargs="?",
const="./",
action="store",
dest="output_directory",
help="extract and store all the APK entries and information retrieved into a given folder (default: './')\n"
"NOTE: this will automatically force the -j / --json option"
)
parser.add_argument(
"-d",
"--verbose",
action="store_true",
dest="verbose",
help="show verbose logs"
)
parser.add_argument(
"-v",
"--version",
action="version",
version=f"NinjaDroid {VERSION}",
help="show version"
)
return parser.parse_args()
def read_file(filepath: str, extended_processing: bool) -> Optional[APK]:
apk = None
logger.debug("Reading %s...", filepath)
try:
apk = ApkParser(logger).parse(filepath, extended_processing)
except ApkParsingError:
logger.error("The target file ('%s') must be an APK package!", filepath)
except FileParsingError:
logger.error("The target file ('%s') must be an existing, readable file!", filepath)
return apk
def get_filename_without_extension(filepath: str) -> str:
filename = os.path.basename(filepath)
if re.search("\\.apk", filepath, re.IGNORECASE):
filename = str(filename[0:-4])
return filename
def setup_output_directory(filepath: str, filename: str) -> str:
if filepath == "./":
filepath += filename
else:
filepath = filepath.rstrip("/")
if not os.path.exists(filepath):
logger.info("Creating %s/...", filepath)
os.makedirs(filepath)
return filepath
if __name__ == "__main__":
sys.exit(main())