-
Notifications
You must be signed in to change notification settings - Fork 57
/
pcqq_DANGER_rekey.py
234 lines (213 loc) · 8.99 KB
/
pcqq_DANGER_rekey.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
print("别用!!!!!!!")
print("必定损坏原始数据库!")
print("rekey 之后是不能用原先的 key 解锁的!")
# print("理论上可能会损坏原数据库,慎用!如果要用请自行去除下一行的 exit()")
exit()
import frida
import sys
import psutil
import shutil
import time
import os
QQ_PID = None
# GUI -> ['C:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQ.exe']
# 要hook的 -> ['C:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQ.exe', '/hosthwnd=2164594', '/hostname=QQ_IPC_{12345678-ABCD-12EF-9976-18373DEAB821}', '/memoryid=0', 'C:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQ.exe']
exit()
for pid in psutil.pids():
p = psutil.Process(pid)
if p.name() == "QQ.exe" and len(p.cmdline()) > 1:
QQ_PID = pid
del p
break
if QQ_PID is None:
print("QQ not launched. exit.")
sys.exit(1)
print("QQ pid is:", QQ_PID)
exit()
demo_script = """
var pMessageBoxW = Module.findExportByName("user32.dll", 'MessageBoxA')
var lpText = Memory.allocAnsiString("I'm New MessageBox");
var funMsgBox = new NativeFunction(pMessageBoxW, 'uint32',['uint32','pointer','pointer','uint32']);
funMsgBox(0, ptr(lpText), ptr(lpText), 0);
"""
hook_script = """
function buf2hex(buffer) {
const byteArray = new Uint8Array(buffer);
const hexParts = [];
for(let i = 0; i < byteArray.length; i++) {
const hex = byteArray[i].toString(16);
const paddedHex = ('00' + hex).slice(-2);
hexParts.push(paddedHex);
}
return '0x' + hexParts.join(', 0x');
}
const kernel_util = Module.load('KernelUtil.dll');
function single_function(pattern) {
pattern = pattern.replaceAll("##", "").replaceAll(" ", "").toLowerCase().replace(/\\s/g,'').replace(/(.{2})/g,"$1 ");
var akey_function_list = Memory.scanSync(kernel_util.base, kernel_util.size, pattern);
if (akey_function_list.length > 1) {
send("pattern FOUND MULTI!!")
send(pattern)
send(akey_function_list)
send("!!exit")
}
if (akey_function_list.length == 0) {
send("pattern NOT FOUND!!")
send("!!exit")
}
return akey_function_list[0]['address'];
}
// const key_function = Module.findExportByName("KernelUtil.dll", 'sqlite3_key')
const key_function = single_function("55 8b ec 56 6b 75 10 11 83 7d 10 10 74 0d 68 17 02 00 00 e8")
const name_function = single_function("55 8B EC FF 75 0C FF 75 08 E8 B8 D1 02 00 59 59 85")
const rekey_function = single_function("##558BEC837D1010740D682F020000E8")
const close_function = single_function("##55 8B EC 56 8B 75 08 85 F6 74 6D 56 E87C 3E 01 00")
const exec_function = single_function("##558BEC8B45088B40505DC3")
// send(key_function)
var name_function_caller = new NativeFunction(name_function, 'pointer', ['pointer', 'pointer']);
var rekey_function_caller = new NativeFunction(rekey_function, 'int', ['pointer', 'pointer', 'int']);
var key_function_caller = new NativeFunction(key_function, 'int', ['pointer', 'pointer', 'int']);
var close_function_caller = new NativeFunction(close_function, 'int', ['pointer', 'int']);
var exec_function_caller = new NativeFunction(exec_function, 'int', ['pointer', 'pointer', 'pointer', 'pointer', 'pointer']);
var TARGET_KEY_LENGTH = 16;
var key_length = 0;
var dbName;
var empty_password = Memory.alloc(TARGET_KEY_LENGTH)
var original_password = Memory.alloc(TARGET_KEY_LENGTH)
empty_password.writeByteArray(Array(TARGET_KEY_LENGTH).fill(0))
const no_sync = "PRAGMA synchronous=ON"
const no_sync_address = Memory.allocUtf8String(no_sync)
const TEST_PWD_SQL = Memory.allocUtf8String("SELECT count(*) FROM sqlite_master;")
var store_args_1, store_args_2, target_db;
var should_copy = false;
var calling_key = false;
var should_show = false;
function is_db_ok(){
return !exec_function_caller(target_db, TEST_PWD_SQL, NULL, NULL, NULL);
}
Interceptor.attach(key_function, {
onEnter: function (args, state) {
if(calling_key){ return; }
should_copy = false;
should_show = false;
console.log("[+] key found:");
dbName = name_function_caller(args[0], NULL).readUtf8String();
if (dbName.replaceAll('/', '\\\\').split('\\\\').pop().toLowerCase() == 'Msg3.0.db'.toLowerCase() || false) {
should_show = true;
target_db = args[0];
// disable memory cache
//console.log("¦- db: " + args[0]);
key_length = args[2].toInt32()
console.log("¦- nKey: " + key_length);
//console.log("¦- pkey: " + args[1]);
console.log("¦- *pkey: " + buf2hex(args[1].readByteArray(key_length)));
console.log("¦- dbName: " + name_function_caller(args[0], NULL).readUtf8String());
//console.log("¦- *pkey: " + buf2hex(Memory.readByteArray(new UInt64(args[1]), key_length)));
if(key_length == TARGET_KEY_LENGTH){
Memory.copy(original_password, args[1], key_length)
should_copy = true;
}
}
},
onLeave: function (retval, state) {
if(calling_key){ return; }
if(!should_show){ return; }
console.log("¦- sqlite3_key return: " + retval);
console.log("¦- is_db_ok: " + is_db_ok());
if (should_copy) {
// exec_function_caller(target_db, no_sync_address, NULL, NULL, NULL);
console.log("rekey to NULL: " + rekey_function_caller(target_db, empty_password, key_length))
console.log("¦- is_db_ok: " + is_db_ok());
// console.log(close_function_caller(target_db, 0))
send("!!MSG3.0: " + dbName)
recv(function(){}).wait();
console.log("rekey to orig: " + rekey_function_caller(target_db, original_password, key_length))
console.log("¦- is_db_ok: " + is_db_ok());
calling_key = true;
// console.log(key_function_caller(target_db, original_password, key_length))
calling_key = false;
}
}
});
var rekey_show_result = false;
Interceptor.attach(rekey_function, {
onEnter: function (args, state) {
console.log("[*] rekey:");
dbName = name_function_caller(args[0], NULL).readUtf8String();
rekey_show_result = false
if (dbName.replaceAll('/', '\\\\').split('\\\\').pop().toLowerCase() == 'Msg3.0.db'.toLowerCase() || false) {
rekey_show_result = true;
//console.log("¦- db: " + args[0]);
key_length = args[2].toInt32()
console.log("¦- nKey: " + key_length);
//console.log("¦- pkey: " + args[1]);
console.log("¦- *pkey: " + buf2hex(args[1].readByteArray(key_length)));
console.log("¦- dbName: " + name_function_caller(args[0], NULL).readUtf8String());
//console.log("¦- *pkey: " + buf2hex(Memory.readByteArray(new UInt64(args[1]), key_length)));
}
},
onLeave: function (retval, state) {
if(!rekey_show_result){ return; }
console.log("¦- sqlite3_rekey return: " + retval);
}
});
"""
session = frida.get_local_device().attach(QQ_PID)
script = session.create_script(hook_script)
message_seq = 0
def on_message(message, data):
global message_seq
if message["type"] == "send":
if message["payload"] == "!!exit":
exit(3)
if message["payload"].startswith("!!MSG3.0: "):
filename = message["payload"][10:]
script.post({})
new_filename = (
filename.split("\\")[-1]
+ "_"
+ str(message_seq)
+ "_"
+ str(time.time()).split(".")[0]
+ ".db"
)
message_seq += 1
print("Copying decrypted file:", filename, "to", new_filename)
shutil.copyfile(filename, new_filename)
file1 = open(new_filename, "rb")
extra_flag = False
# detect extra sqlite header "SQLite header 3"
if file1.read(15) == b"SQLite header 3":
file1.seek(1024)
if file1.read(15) == b"SQLite format 3":
file1.seek(1024)
extra_flag = True
print("NEW PLAIN TEXT DB detected!")
if not extra_flag:
file1.seek(0)
print("hmm db seems still encrypted... anything wrong?")
file1.close()
# remove extra 1024 bytes header of a huge file
if extra_flag:
file1 = open(new_filename, "rb")
file2 = open(new_filename + ".tmp", "wb")
file1.seek(1024)
shutil.copyfileobj(file1, file2)
file1.close()
file2.close()
os.remove(new_filename)
os.rename(new_filename + ".tmp", new_filename)
print("Done. File Path:")
print(os.path.abspath(new_filename))
else:
print(message["payload"])
elif message["type"] == "error":
print(message["stack"])
def on_destroyed():
print("process exited.")
os._exit(0)
script.on("message", on_message)
script.on("destroyed", on_destroyed)
script.load()
print("hooked.")
sys.stdin.read()