-
Notifications
You must be signed in to change notification settings - Fork 0
/
parseReact.py
169 lines (141 loc) · 6.24 KB
/
parseReact.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import jsbeautifier, re, os, sys
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-b", "--beauty", action="store_true",
help="DO NOT beautify the bundle. This is used when you already have the output from js-beautify. It requires the --file option.")
parser.add_argument("-f", "--file", default="bundle.js", nargs='?', action="store",
help="File name of the bundle file. Default name (if no --beauty option is used): bundle.js")
parser.add_argument("-g", "--bridge", default="NativeModules.Contacts", nargs='?', action='store',
help="Specify the full bridge name (eg. for NativeModules.Contacts, insert NativeModules.Contacts). Default: NativeModules.Contacts")
args = parser.parse_args()
HEADER = '\033[95m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
# Keywords to search in the functions
keywords = ['http.get', 'http.post','upload', 'sendrequest', 'xmlhttprequest', '$.ajax', '$.post', 'fetch', '$http',
'"post"', 'firebasestorage', 'firebaseio', 'send', 'contact', 'invite', 'getAll']
global signals
signals = False
# Get the bridge function
bridge_func_regex = re.compile(r'(?s)'+args.bridge+'((.*?)|(\n)+)},.\d+,')
# Get the bridge ID
bridge_id_regex = re.compile(r',.\d+,')
# Separate all functions into Match Objects
functions_sep_regex = re.compile(r'(?s)__d\(function\((\w+,\s)+\w+\)\s\{(.*?)\},\s\d+(.*?)\d\]\)\;')
# To parse the bridge names
bridge_names_regex = re.compile(r'NativeModules\.\w+')
# To count the bridge number used in the function
bridge_line_regex = re.compile(r'(?:\d+,.)+\d+')
# To parse bad parsed functions
def beautify(bundleName):
try:
print('[+] Beautifier on the move, please be patient...')
file_exists=os.path.exists(bundleName)
if not file_exists:
sys.exit(1)
res = jsbeautifier.beautify_file(bundleName)
with open("bundle_beauty.js", "w") as f:
f.write(res)
print('[+] Beautiful bundle in "bundle_beauty.js"')
bridge_func = bridge_func_regex.search(res)
all_bridges_func(res)
except SystemExit:
print (FAIL+"ERROR! FILE NOT FOUND."+ENDC)
sys.exit(1)
except:
pass #jsbeautifier gives some random errors sometimes due to imports
else:
return res
def openBundleBeauty(bundleName):
print("[+] Opening file " + bundleName)
flag = False
try:
with open(bundleName, 'r') as file:
res = file.read()
all_bridges_func(res)
flag = True
except:
print (FAIL+"ERROR! FILE NOT FOUND."+ENDC)
sys.exit(1)
else:
return res
def all_bridges_func (res):
print("[+] Writing all bridges names (starting with NativeModules.) in: all_bridges.txt")
filename = "all_bridges.txt"
file_exists=os.path.exists(filename)
if file_exists:
os.remove(filename)
all_bridges = bridge_names_regex.finditer(res)
for bridges in all_bridges:
with open(filename, "a") as file:
file.write(bridges[0] + "\n")
uniqlines = set(open(filename).readlines())
open(filename, 'w').writelines(set(uniqlines))
def parse(res):
print('[+] Searching for the bridge ID...')
bridge_func = bridge_func_regex.search(res)
try:
bridge_id = bridge_id_regex.search(bridge_func[0])
except:
print (FAIL+"ERROR! BRIDGE NAME INCORRECT OR NOT FOUND."+ENDC)
sys.exit(1)
print('[+] Bridge ID found: '+OKGREEN+bridge_id[0].replace(',','')+ ENDC)
print('[+] Finding functions with that bridge ID...')
functions_sep = functions_sep_regex.finditer(res)
count = 0
numberOfFuncFound=len(re.findall(bridge_id[0], res))
for functions in functions_sep:
if bridge_id[0] in functions.group(0):
func_name = 'function_'+str(count)+'.js'
count = count + 1
print('[+] Function found! Writing it in: '+ OKCYAN + func_name + ENDC)
file_exists=os.path.exists(func_name)
if file_exists:
os.remove(func_name)
with open(func_name, "w") as file:
function_beauty = jsbeautifier.beautify(functions.group(0))
file.write(function_beauty)
id_count_helper (func_name, bridge_id)
# Maybe call here the analyse_files
if not signals:
print('\t[-] No suspicious patterns found in this function.')
if count!=numberOfFuncFound or count <= 1: # The bridge function is usually captured, and at least two functions should be captured (except if the bridge is not used)
print(WARNING+'[!!] ATENTION: THE FUNCTIONS WERE PROBABLY NOT PARSED PROPERLY, CHECK THE BUNDLE MANUALLY!! (known bug, catastrophic backtracking)'+ENDC)
else:
print('[+] DONE! ANALYSE ALL THE FUNCTIONS AND THE BUNDLE MANUALLY TOO!')
def id_count_helper(func_name, bridge_id):
with open(func_name, "r") as file:
for line in file:
pass
bridge_line = bridge_line_regex.search(line)
try:
list = bridge_line[0].split (",")
print('\t[-] The index in the import array is: '+HEADER + str(list.index(bridge_id[0].replace(',','')))+ ENDC)
analyse_files(func_name)
except:
print('\t[-] This is the bridge function. ')
pass # The bridge function will fail
def analyse_files(file_name):
global signals
verdict = WARNING+'\t[!] ATENTION'+ENDC+': File ' + file_name + ' needs MORE analysis. Found: '+ENDC
with open(file_name, "r") as file:
for line in file:
for keyword in keywords:
if keyword.lower() in line.lower() and keyword.lower() not in verdict:
verdict += WARNING+keyword + ' '+ENDC
signals = True
if signals and not verdict.endswith(': '):
print(verdict)
if args.beauty and (args.file is None or args.file == "bundle.js"):
parser.error(FAIL+"ERROR: --beauty requires --file to specify the file."+ENDC)
if args.beauty:
if (args.file is None or args.file == "bundle.js"):
parser.error("--beauty requires --file to specify the file.")
res=openBundleBeauty(args.file)
parse(res)
else:
res=beautify(args.file)
parse(res)