moonD4rk / HackBrowserData

Extract and decrypt browser data, supporting multiple data types, runnable on various operating systems (macOS, Windows, Linux).
MIT License
10.68k stars 1.54k forks source link

360 Speed Browser cannot decrypt history data #155

Open stokovv opened 2 years ago

stokovv commented 2 years ago

Describe the bug Use ./hack-browser-data -vv paste result here

[NOTICE] [browser.go:72,pickChromium] find browser 360speed_default success
[INFO] [chromium_windows.go:33,GetMasterKey] 360speed_default initialized master key success
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [cookie.go:80,Parse] cipher: message authentication failed
[ERROR] [browsingdata.go:42,Recovery] parse creditcard error no such column: nickname
[NOTICE] [browsingdata.go:65,Output] output to file results/360speed_default_cookie.csv success
[NOTICE] [browsingdata.go:65,Output] output to file results/360speed_default_extension.csv success
[NOTICE] [browsingdata.go:65,Output] output to file results/360speed_default_bookmark.csv success

Desktop (please complete the following information):

Additional context Add any other context about the problem here. 参考360社区,从版本13开始,360safe和360speed对历史记录进行加密,History文件转为加密的360History文件,是否和这个有关?

yourchanges commented 6 months ago

这里有一个破解加密的python实现,但是需要获取对应机器的GUID 和账号id

'''
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)