360安全浏览器历史记录、收藏夹
解析360History、360Bookmarks
目录
版本信息
360se13.1.6055.0
User Data\Default\360History 加密的sqlite数据库
User Data\Default\360Bookmarks 加密的收藏夹信息
数据库key生成
dataone:
stub
ws_sid
/?
ws_mid MachineGuid
mhash_encbs
BuildPatentHash_(dataone,md5_hex(dataone))-->mhashbs
mhash_encbs=urlenc(b64encode(mhashbs))
datatwo:
1
sizeof(stub)
stub
2
sizeof(mhash_encbs)
mhash_encbs
key:
p1=md5(randenc(datatwo)).hexdigest())
key=md5(tea360(p1).hexdigest()
getkey_12537D60
[#]genp1_12539140
生成dataone
生成p1
生成mhash_encbs
hash360
https://github.com/276793422/CalcHash_CS64
组合datatwo
rand_enc_12538880
md5 randenc_data -->p1
[#]genkey_12537BA0
tea_1253AF90
tea_encrypto_1253B2D0
md5 tea_data-->key
bookmark
iv="33mhsq0uwgzblwdo"
py
参考链接
http://liulanqicode.com/360bookmarks.htm
前人栽树后人乘凉,分析history的时候看到了上面链接分析bookmarks,发现key是一致的。
2020.07.25冲冲冲
py:
'''
python -m pip install wmi
python -m pip install pycryptodome
'''
import base64
from ctypes import *
import binascii
import hashlib
import os
import winreg
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import wmi
def _ZIMU(d1, d2):
return c_uint32(d1*d2).value
def _ZOO3(d1, d2):
d1 = c_uint32(d1)
d2 = c_uint32(d2)
return _ZIMU(d2.value, c_uint32(d1.value >> 16).value)
def _ZOO1(d1, d2, d3):
return c_uint32(_ZIMU(d2, d1) - _ZOO3(d1, d3)).value
def _ZOO2(d1, d2, d3):
return c_uint32(_ZIMU(d2, d1) + _ZOO3(d1, d3)).value
def CS64_WordSwap(pSrcStr: list, iChNum: int, pdwMd5: list, pOutInt: list) -> bool:
if iChNum < 2 or iChNum & 1:
return False
dwMD5_0 =c_uint32((pdwMd5[0] | 1) + 0x69FB0000) .value
dwMD5_1 = c_uint32((pdwMd5[1] | 1) + 0x13DB0000).value
dwRet1 = 0
dwRet0 = 0
i=0
while i<iChNum:
dwTemp00 = dwRet0
if (i < iChNum):
dwTemp00 += pSrcStr[i]
dwTemp00=c_uint32(dwTemp00).value
dwTemp01 = _ZOO1(dwTemp00, dwMD5_0, 0x10FA9605)
dwTemp02 = _ZOO2(dwTemp01, 0x79F8A395, 0x689B6B9F)
dwTemp03 = _ZOO1(dwTemp02, 0xEA970001, 0x3C101569)
i+=1
dwTemp04 = dwTemp03
if (i < iChNum):
dwTemp04 += pSrcStr[i]
dwTemp04=c_uint32(dwTemp04).value
dwTemp05 = _ZOO1(dwTemp04, dwMD5_1, 0x3CE8EC25)
dwTemp06 = _ZOO1(dwTemp05, 0x59C3AF2D, 0x2232E0F1)
dwTemp07 = _ZOO2(dwTemp06, 0x1EC90001, 0x35BD1EC9)
i+=1
dwRet0 = dwTemp07
dwRet1 = c_uint32(dwTemp03 + dwRet0 + dwRet1).value
pOutInt[0] = dwRet0
pOutInt[1] = dwRet1
print('CS64_WordSwap pOutInt:',','.join(map(hex,pOutInt)))
return True
def CS64_Reversible(pSrcStr: list, iChNum: int, pdwMd5: list, pOutInt: list) -> bool:
if (iChNum < 2 or iChNum & 1):
return False
dwRet1 = 0
dwRet0 = 0
dwMD5_0 = pdwMd5[0] | 1
dwMD5_1 = pdwMd5[1] | 1
i=0
while i <iChNum:
dwTemp00 = dwRet0
if i < iChNum:
dwTemp00 += pSrcStr[i]
dwTemp00=c_uint32(dwTemp00).value
dwTemp01 = c_uint32(dwMD5_0 * dwTemp00).value
dwTemp02 = _ZOO1(dwTemp01, 0xB1110000, 0x30674EEF)
dwTemp03 = _ZOO1(dwTemp02, 0x5B9F0000, 0x78F7A461)
dwTemp04 = _ZOO2(dwTemp03, 0xB96D0000, 0x12CEB96D)
dwTemp05 = _ZOO2(dwTemp04, 0x1D830000, 0x257E1D83)
i+=1
dwTemp06 = dwTemp05
if (i < iChNum):
dwTemp06 += pSrcStr[i]
dwTemp06=c_uint32(dwTemp06).value
dwTemp07 = c_uint32(dwMD5_1 * dwTemp06).value
dwTemp08 = _ZOO1(dwTemp07, 0x16F50000, 0x5D8BE90B)
dwTemp09 = _ZOO1(dwTemp08, 0x96FF0000, 0x2C7C6901)
dwTemp10 = _ZOO2(dwTemp09, 0x2B890000, 0x7C932B89)
dwTemp11 = _ZOO1(dwTemp10, 0x9F690000, 0x405B6097)
i+=1
dwRet0 = dwTemp11
dwRet1 = c_uint32(dwTemp05 + dwRet0 + dwRet1).value
pOutInt[0] = dwRet0
pOutInt[1] = dwRet1
print('CS64_Reversible pOutInt:',','.join(map(hex,pOutInt)))
return True
def BuildPatentHash_(pWsStr:bytes, pMd5:list, pOut:list):
dwWsLen=len(pWsStr)
dwCount = dwWsLen // 4
if (dwCount & 1):
dwCount -= 1
r1 = [0,0]
r2 = [0,0]
plist=[]
for i in range(dwCount):
plist.append(int.from_bytes(pWsStr[i*4:i*4+4],'little') )
if (not CS64_WordSwap(plist, dwCount, pMd5, r1) or not CS64_Reversible(plist, dwCount, pMd5, r2)):
return False
for i in range(len(r1)):
pOut.append(r1[i] ^ r2[i])
return True
def getmd5hex_bs(bs: bytes) -> bytes:
md5ch = hashlib.md5(bs)
md5str = md5ch.hexdigest()
print(md5str)
return md5ch.digest()
def mteaencrypt(v, k):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = 0x61C88647
k0, k1, k2, k3 = k[0], k[1], k[2], k[3]
total = c_uint32(0x9E3779B9)
for i in range(8):
# total.value += delta
v0.value += ((v1.value << 4) + k0) ^ (v1.value + total.value) ^ ((v1.value >> 5) + k1)
v1.value += ((v0.value << 4) + k2) ^ (v0.value + total.value) ^ ((v0.value >> 5) + k3)
total.value -= delta
# print(hex(total.value))
return v0.value, v1.value
def mteadecrypt(v, k):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = 0x61C88647 # 0x9e3779b9
k0, k1, k2, k3 = k[0], k[1], k[2], k[3]
total = c_uint32(0x8ff34781) # 0x61C88647
for i in range(8):
total.value += delta
v1.value -= ((v0.value << 4) + k2) ^ (v0.value + total.value) ^ ((v0.value >> 5) + k3)
v0.value -= ((v1.value << 4) + k0) ^ (v1.value + total.value) ^ ((v1.value >> 5) + k1)
return v0.value, v1.value
def tea360(databs: bytes, size: int = 0x80) -> bytes: # tes 128
dsz = len(databs)
if dsz % 4 != 0 or size % 4 != 0:
print('len error!')
return None
if size > dsz:
# print('pad to ',size)
databs += b'\x00'*(size-dsz)
dsz = size
idatas = []
keys = []
for i in range(0, dsz, 4):
inum = int.from_bytes(databs[i:i+4], 'little')
if i < 16:
keys.append(inum)
idatas.append(inum)
bs = b''
for j in range(0, len(idatas), 2):
x, y = mteaencrypt(idatas[j:j+2], keys)
bs += x.to_bytes(4, 'little')
bs += y.to_bytes(4, 'little')
return bs
def getUnicode_bs(s:str)->bytes:
return s.encode('UTF-16LE')
def getMID()->str:
MachineGuidkey = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Cryptography",
0,
winreg.KEY_READ | winreg.KEY_WOW64_64KEY
)
MachineGuidstr = winreg.QueryValueEx(MachineGuidkey, "MachineGuid")[0]
print('MachineGuidstr:',MachineGuidstr)
return MachineGuidstr
def getSID()->str:
mwmi=wmi.WMI()
us=mwmi.Win32_UserAccount()
for u in us:
if u.Name==os.getlogin():
print(u.Name,u.SID)
return u.SID
def rand(myseed):
n = myseed * 0x343fd + 0x269ec3
myseed = n & 0xffffffff
return myseed
def randenc(data: bytes, seed: int = 0x8000402B) -> bytes:
out = seed.to_bytes(4, 'little')
sz = len(data)
n = sz % 4
if n:
m = sz//4*4
else:
m = sz
for i in range(0, m, 4):
seed = rand(seed)
bs = seed.to_bytes(4, 'little')
for j in range(4):
out += (data[i+j] ^ bs[j]).to_bytes(1, 'little')
seed = rand(seed)
for i in range(n):
bs = seed.to_bytes(4, 'little')
out += (data[m+i] ^ bs[i]).to_bytes(1, 'little')
return out
def urlenc(bs:bytes)->bytes:
ret=b''
unreservedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.:/"
for x in bs:
if chr(x) in unreservedChars:
ret+=x.to_bytes(1,'little')
elif chr(x)==' ':
ret+=b'+'
else:
ret+=b'%%%02x'%x
return ret
#148 (0x94)
stub=[
0x1F, 0x7D, 0x14, 0x89, 0x4D, 0xB8, 0x8B, 0x4D, 0x18, 0x89, 0x45, 0xBC, 0x89, 0x7D, 0xB0, 0x89,
0x4D, 0xAC, 0x89, 0x5D, 0xD0, 0x89, 0x5D, 0xC8, 0x88, 0x5D, 0xD7, 0x88, 0x5D, 0xC0, 0x3B, 0xFB,
0x0F, 0x84, 0x77, 0x93, 0x03, 0x00, 0x89, 0x90, 0x8B, 0x4E, 0x10, 0xF7, 0x41, 0x08, 0x00, 0x40,
0x00, 0x00, 0x0F, 0x84, 0x6F, 0x93, 0x03, 0x00, 0xF7, 0x46, 0x68, 0x00, 0x01, 0x00, 0x00, 0x0F,
0x85, 0x58, 0xCC, 0x03, 0x00, 0xF6, 0x05, 0xA0, 0x03, 0xFE, 0x7F, 0x01, 0xBF, 0x00, 0x01, 0x00,
0x02, 0x0F, 0x85, 0x4F, 0x96, 0x03, 0x00, 0x89, 0x5D, 0xD8, 0x85, 0x7E, 0x68, 0x0F, 0x85, 0x8B,
0x96, 0x03, 0x00, 0x38, 0x5E, 0x02, 0x0F, 0x85, 0xEA, 0x96, 0xCC, 0x00, 0xEB, 0x1D, 0x39, 0x5D,
0xC8, 0x0F, 0x85, 0x3B, 0x97, 0x03, 0x00, 0x8B, 0x45, 0xD8, 0x8B, 0x4D, 0xFC, 0x5F, 0x5E, 0x33,
0x10, 0x00, 0x00, 0x00, 0x57, 0x00, 0x44, 0x00, 0x4C, 0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00,
0x36, 0x00, 0x00, 0x00
]
stub=bytes(stub)
ploadone=b''
ploadone+=stub
ploadone+=getUnicode_bs(getSID())
ploadone+=getUnicode_bs('/?')
ploadone+=getUnicode_bs(getMID())
print('ploadone:',binascii.b2a_hex(ploadone))
md5ploadone_bs=getmd5hex_bs(ploadone)
a=int.from_bytes(md5ploadone_bs[:4],'little')
print('a:',hex(a))
b=int.from_bytes(md5ploadone_bs[4:8],'little')
print('b:',hex(b))
imd5=[a,b]
pout=[]
BuildPatentHash_(ploadone,imd5,pout)
mhashbs=pout[0].to_bytes(4, 'little')
mhashbs+=pout[1].to_bytes(4, 'little')
print('pout:',','.join(map(hex,pout)),mhashbs)
mhash_encbs=urlenc(base64.b64encode(mhashbs))
print(mhash_encbs)
ploadtwo=b''
ploadtwo+=int.to_bytes(1,4,'little')
ploadtwo+=int.to_bytes(len(stub),4,'little')
ploadtwo+=stub
ploadtwo+=int.to_bytes(2,4,'little')
ploadtwo+=int.to_bytes(len(mhash_encbs),4,'little')
ploadtwo+=mhash_encbs
print('ploadtwo:',binascii.b2a_hex(ploadtwo))
randenc_bs = randenc(ploadtwo)
print('[#]gen p1')
tmp_bs = getmd5hex_bs(randenc_bs)
tmp_bs = tea360(binascii.b2a_hex(tmp_bs))
tmp_bs = pad(tmp_bs, 0xc0+1, 'x923')[:-1]
print(binascii.b2a_hex(tmp_bs))
print('\n#######################################################################################################')
print('[#]360History sqlite db key:')
key_bs = getmd5hex_bs(tmp_bs)
print('#######################################################################################################\n')
# bookmark
aesch=AES.new(key=binascii.b2a_hex(key_bs),iv=b'33mhsq0uwgzblwdo',mode=AES.MODE_CBC)
bookmark=b''
with open('360Bookmarks','rb') as f:
bookmark=f.read()
bs=base64.b64decode(bookmark)[4:]
bs=aesch.decrypt(bs)
print(bs.decode())
with open('bookmark.txt','wb') as f:
f.write(bs)