Skip to content

Commit

Permalink
Help for boredom in covid19 times. Enjoy !
Browse files Browse the repository at this point in the history
  • Loading branch information
bkerler committed Apr 15, 2020
1 parent 57a1368 commit 884a278
Show file tree
Hide file tree
Showing 3 changed files with 269 additions and 12 deletions.
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
# oppo_decrypt
Oppo/Oneplus .ops Firmware decrypter
Oppo .ops Firmware decrypter
------------------------------------

Tested with "MSMDownloadTool V4.0" for Oneplus 5/6, Frida >10.4 and Windoze
Works for oppo only, not oneplus !

* backdoor.py : Enables hidden "readback" functionality
* ops_decrypt.py : Decrypts any part of the firmware with .ops extension, needs "MsmDownload Tool" and frida
* ops_extract.py : Decrypts any part of the firmware with .ops extension
* ofp_decrypt.py : Decrypts any part of the firmware with .ofp extension


Based on Frida.re and python 3.6
Based on python 3.x

Installation:
-------------
'pip install frida'
pip3 install pycrypto


Windows only, sorry folks !
Both Linux and Windows now, folks !

Usage:
--------
* Oneplus 5 QD-Loader decryption:
'python decrypt.py "MsmDownloadTool V4.0.exe" 0 0x92880'
* Enable readback mode (use admin command prompt under windoze):
```
python3 backdoor.py "MsmDownloadTool V4.0.exe"'
```

* Extract ops file:

* Enable readback mode:
'python backdoor.py "MsmDownloadTool V4.0.exe"'
```
python3 ops_extract.py [myops.ops]
```

License:
--------
Share, modify and use as you like, but refer the original author !
Share, modify and use as you like, but refer the original author !
File renamed without changes.
252 changes: 252 additions & 0 deletions ops_extract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
#!/usr/bin/env python3
# (c) B.Kerler, MIT license
import os
import sys
import xml.etree.ElementTree as ET
from struct import unpack
from binascii import unhexlify, hexlify
from Crypto.Cipher import AES
from Crypto.Hash import MD5
import hashlib
import shutil

def swap(ch):
return ((ch & 0xF) << 4) + ((ch & 0xF0) >> 4)


def keyshuffle(key, hkey):
for i in range(0, 0x10, 4):
key[i] = swap((hkey[i] ^ key[i]))
key[i + 1] = swap(hkey[i + 1] ^ key[i + 1])
key[i + 2] = swap(hkey[i + 2] ^ key[i + 2])
key[i + 3] = swap(hkey[i + 3] ^ key[i + 3])
return key

def ROR(x, n, bits = 32):
mask = (2**n) - 1
mask_bits = x & mask
return (x >> n) | (mask_bits << (bits - n))

def ROL(x, n, bits = 32):
return ROR(x, bits - n, bits)

def generatekey1():
key1 = "42F2D5399137E2B2813CD8ECDF2F4D72"
key2 = "F6C50203515A2CE7D8C3E1F938B7E94C"
key3 = "67657963787565E837D226B69A495D21"

key1 = bytearray.fromhex(key1)
key2 = bytearray.fromhex(key2)
key3 = bytearray.fromhex(key3)

key2 = keyshuffle(key2, key3)
aeskey = bytes(hashlib.md5(key2).hexdigest()[0:16], 'utf-8')
key1 = keyshuffle(key1, key3)
iv = bytes(hashlib.md5(key1).hexdigest()[0:16], 'utf-8')
return aeskey,iv

def generatekey2(filename):
keys = [
#R9s/A57t
["V1.4.17/1.4.27", "27827963787265EF89D126B69A495A21","82C50203285A2CE7D8C3E198383CE94C","422DD5399181E223813CD8ECDF2E4D72"],
["V1.5.13", "67657963787565E837D226B69A495D21","F6C50203515A2CE7D8C3E1F938B7E94C","42F2D5399137E2B2813CD8ECDF2F4D72"],
#R15 Pro CPH1831 V1.6.6 / FindX CPH1871 V1.6.9 / R17 Pro CPH1877 V1.6.17 / R17 PBEM00 V1.6.17 / A5 2020 V1.7.6 / K3 CPH1955 V1.6.26 UFS
#Reno 5G CPH1921 V1.6.26 / Realme 3 Pro RMX1851 V1.6.17 / Reno 10X Zoom V1.6.26 / R17 CPH1879 V1.6.17 / R17 Neo CPH1893 / K1 PBCM30
["V1.6.6/1.6.9/1.6.17/1.6.24/1.6.26/1.7.6", "3C2D518D9BF2E4279DC758CD535147C3","87C74A29709AC1BF2382276C4E8DF232","598D92E967265E9BCABE2469FE4A915E"],
#a3s
["V1.6.17", "E11AA7BB558A436A8375FD15DDD4651F","77DDF6A0696841F6B74782C097835169","A739742384A44E8BA45207AD5C3700EA"],
#RM1921EX V1.7.2, Realme X RMX1901 V1.7.2, Realme 5 Pro RMX1971 V1.7.2, Realme 5 RMX1911 V1.7.2
["V1.7.2", "8FB8FB261930260BE945B841AEFA9FD4","E529E82B28F5A2F8831D860AE39E425D","8A09DA60ED36F125D64709973372C1CF"]
]

for dkey in keys:
key = bytearray()
iv = bytearray()
# "Read metadata failed"
mc = bytearray.fromhex(dkey[1])
userkey=bytearray.fromhex(dkey[2])
ivec=bytearray.fromhex(dkey[3])

#userkey=bytearray(unhexlify("A3D8D358E42F5A9E931DD3917D9A3218"))
#ivec=bytearray(unhexlify("386935399137416B67416BECF22F519A"))
#mc=bytearray(unhexlify("9E4F32639D21357D37D226B69A495D21"))

for i in range(0,len(userkey)):
v=ROL((userkey[i]^mc[i]),4,8)
key.append(v)

for i in range(0,len(userkey)):
v=ROL((ivec[i]^mc[i]),4,8)
iv.append(v)

h=MD5.new()
h.update(key)
key=h.digest()

h = MD5.new()
h.update(iv)
iv = h.digest()

key=hexlify(key).lower()[0:16]
iv=hexlify(iv).lower()[0:16]
pagesize,data=extract_xml(filename,key,iv)
if pagesize!=0:
return pagesize,key,iv,data
return 0,None,None,None


def extract_xml(filename,key,iv):
filesize=os.stat(filename).st_size
with open(filename,'rb') as rf:
pagesize = 0x200
xmloffset=filesize-pagesize
rf.seek(xmloffset+0x10)
if unpack("<I",rf.read(4))[0]==0x7CEF:
pagesize=0x200
else:
pagesize=0x1000
xmloffset=filesize-pagesize
rf.seek(xmloffset + 0x10)
magic=unpack("<I", rf.read(4))[0]
if not magic == 0x7CEF:
print("Unknown pagesize. Aborting")
exit(0)

rf.seek(xmloffset+0x14)
offset=unpack("<I",rf.read(4))[0]*pagesize
length=unpack("<I",rf.read(4))[0]

rf.seek(offset)
data=rf.read(length)
dec=aes_cfb(data,key,iv)

#h=MD5.new()
#h.update(data)
#print(dec.decode('utf-8'))
#print(h.hexdigest())
#print("Done.")
if b"<?xml" in dec:
return pagesize,dec
else:
return 0,""

def aes_cfb(data,key,iv):
ctx = AES.new(key, AES.MODE_CFB, iv=iv, segment_size=128)
decrypted = ctx.decrypt(data)
return decrypted

def copysub(rf,wf,start,length):
rf.seek(start)
rlen=0
while (length > 0):
if length < 0x100000:
size = length
else:
size = 0x100000
data = rf.read(size)
wf.write(data)
rlen+=len(data)
length -= size
return rlen

def decryptfile(key,iv,filename,path,wfilename,start,length,rlength,decryptsize=0x40000):
print(f"Extracting {wfilename}")
with open(filename, 'rb') as rf:
with open(os.path.join(path, wfilename), 'wb') as wf:
rf.seek(start)
if length>decryptsize:
size=decryptsize
else:
size=length
data=rf.read(size)
if size%4:
data+=(4-(size%4))*b'\x00'
outp = aes_cfb(data, key, iv)
if size==decryptsize:
wf.write(outp[:size])
rlength-=size
if rlength>0:
copysub(rf,wf,start+decryptsize,rlength)
else:
wf.write(outp[:rlength])

def main():
if len(sys.argv)<2:
print("Usage: ./ofp_libextract.py [Filename.ofp]")
exit(0)

filename=sys.argv[1]
filesize=os.stat(filename).st_size

#key,iv=generatekey1()
pagesize,key,iv,data=generatekey2(filename)
if pagesize==0:
print("Unknown key. Aborting")
exit(0)
else:
#print(data.decode('utf-8'))
xml=data[:data.rfind(b">")+1].decode('utf-8')

if "/" in filename:
path = filename[:filename.rfind("/")]
elif "\\" in filename:
path = filename[:filename.rfind("\\")]
else:
path = ""

path = os.path.join(path,"extract")

if os.path.exists(path):
shutil.rmtree(path)
os.mkdir(path)
else:
os.mkdir(path)

root = ET.fromstring(xml)
for child in root:
if child.tag == "Sahara":
for item in child:
if item.tag == "File":
wfilename = item.attrib["Path"]
start = int(item.attrib["FileOffsetInSrc"]) * pagesize
length = int(item.attrib["SizeInSectorInSrc"]) * pagesize
rlength = int(item.attrib["SizeInByteInSrc"])
decryptfile(key, iv, filename, path, wfilename, start, length, rlength,rlength)
elif child.tag in ["Config","Provision","ChainedTableOfDigests","DigestsToSign"]:
for item in child:
if item.tag == "config":
wfilename = item.attrib["filename"]
start = int(item.attrib["SizeInSectorInSrc"]) * pagesize
length = int(item.attrib["SizeInByteInSrc"])
decryptfile(key,iv,filename, path, wfilename, start, length,length)
elif "Program" in child.tag:
# if not os.path.exists(os.path.join(path, child.tag)):
# os.mkdir(os.path.join(path, child.tag))
# spath = os.path.join(path, child.tag)
for item in child:
if "filename" in item.attrib:
wfilename = item.attrib["filename"]
if wfilename == "":
continue
start = int(item.attrib["FileOffsetInSrc"]) * pagesize
length = int(item.attrib["SizeInSectorInSrc"]) * pagesize
rlength = int(item.attrib["SizeInByteInSrc"])
decryptfile(key,iv,filename, path, wfilename, start, length,rlength)
else:
for subitem in item:
if "filename" in subitem.attrib:
wfilename = subitem.attrib["filename"]
if wfilename == "":
continue
start = int(subitem.attrib["FileOffsetInSrc"]) * pagesize
length = int(subitem.attrib["SizeInSectorInSrc"]) * pagesize
rlength = int(item.attrib["SizeInByteInSrc"])
decryptfile(key,iv,filename, path, wfilename, start, length,rlength)
# else:
# print (child.tag, child.attrib)
print("Done. Extracted files to " + path)
exit(0)


if __name__=="__main__":
main()

0 comments on commit 884a278

Please sign in to comment.