commit
6ab756ae55
24 changed files with 944 additions and 172 deletions
57
README.md
57
README.md
|
|
@ -3,23 +3,56 @@ Validity fingerprint sensor library.
|
|||
Originally designed to capture some of my findings for 138a:0097, but if you manage to get it working for some other Validity sensor - pull requests are welcome.
|
||||
|
||||
## Setting up
|
||||
Use Wine to extract [official Lenovo device driver](https://support.lenovo.com/es/es/downloads/DS120491) (also [part of the SCCM package](https://support.lenovo.com/ec/th/downloads/DS112113)). The only reason you need to do this is to find `6_07f_lenovo_mis.xpfwext` and copy it to this project location.
|
||||
|
||||
You can just install that .exe and your file will be in `$WINEPREFIX/drive_c/DRIVERS/WIN/FPR/WBF_Drivers/6_07f_Lenovo.xpfwext` in case you use the device driver, or in
|
||||
`$WINEPREFIX/drive_c/DRIVERS/SCCM/tp_t460s_*/*/Security/MT7FP22W/6_07f_Lenovo.xpfwext` if you used the SCCM package.
|
||||
|
||||
Otherwise just use [innounp](https://sourceforge.net/projects/innounp/files/latest/download) and extract the exe file (`n1cgn08w.exe` in the device driver case or `tp_t460s_*.exe` in the SSCM one) with a command such as:
|
||||
|
||||
WINEPREFIX=$XDG_RUNTIME_DIR/tmpwine wine ./innounp.exe -x -e n1cgn08w.exe 6_07f_Lenovo.xpfwext
|
||||
|
||||
This file contains firmware and without it the pairing wont work. You will not need wine after this.
|
||||
|
||||
To install Python dependencies run
|
||||
```
|
||||
$ pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
## Factory reset
|
||||
## Initialization
|
||||
|
||||
### Automatic factory reset, pairing and firmware flashing
|
||||
|
||||
This repo includes `validity-sensors-initializer.py`, a simple tool that
|
||||
helps initializing Validity fingerprint readers under linux, loading their
|
||||
binary firmware and initializing them.
|
||||
|
||||
This tool currently only supports these sensors:
|
||||
- 138a:0090 Validity Sensors, Inc. VFS7500 Touch Fingerprint Sensor
|
||||
- 138a:0097 Validity Sensors, Inc.
|
||||
Which are present in various ThinkPad and HP laptops.
|
||||
|
||||
These devices communicate with the laptop via an encrypted protocol and they
|
||||
need to be paired with the host computer in order to work and compute the
|
||||
TLS keys.
|
||||
Such initialization is normally done by the Windows driver, however thanks to
|
||||
the amazing efforts of Viktor Dragomiretskyy (uunicorn), and previously of
|
||||
Nikita Mikhailov, we have reverse-engineerd the pairing process, and so it's
|
||||
possible to do it under Linux with only native tools as well.
|
||||
|
||||
The procedure is quite simple:
|
||||
- Device is factory-reset and its flash repartitioned
|
||||
- A TLS key is negotiated, generated via host hw ID and serial
|
||||
- Windows driver is downloaded from Lenovo to extract the device firmware
|
||||
- The device firmware is uploaded to the device
|
||||
- The device is calibrated
|
||||
|
||||
Once the chip is paired with the computer via this tool, it's possible to use
|
||||
it in libfprint using the driver at
|
||||
- https://github.com/3v1n0/libfprint/
|
||||
|
||||
---
|
||||
|
||||
### Getting the firmware
|
||||
|
||||
It's possible to just extract [official Lenovo device driver for vfs0097](https://support.lenovo.com/us/en/downloads/DS121407) or [driver for vfs0090](https://support.lenovo.com/us/en/downloads/DS120491) (also [part of the SCCM package](https://support.lenovo.com/ec/th/downloads/DS112113) using [innoextract](https://constexpr.org/innoextract/) (available for all the distros), or `wine`.
|
||||
|
||||
The only reason you need to do this is to find `6_07f_lenovo_mis.xpfwext` (for vfs0097) or `6_07f_Lenovo.xpfwext` (for vfs0090) and copy it to this project location.
|
||||
|
||||
innoextract n1mgf03w.exe -e -I 6_07f_lenovo_mis.xpfwext # vfs0097
|
||||
innoextract n1cgn08w.exe -e -I 6_07f_Lenovo.xpfwext # vfs0090
|
||||
|
||||
### Factory reset
|
||||
If your device was previously paired with another OS or computer, you need to do a factory reset.
|
||||
This will erase all fingers from the internal database and make the device ready for pairing.
|
||||
```
|
||||
|
|
@ -27,7 +60,7 @@ $ python3 factory-reset.py
|
|||
$
|
||||
```
|
||||
|
||||
## Pairing
|
||||
### Pairing
|
||||
After performing a factory reset you need to pair your device with a host computer.
|
||||
This must be done only once, before you can enroll/identify/verify fingers.
|
||||
```
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ from pydbus.generic import signal
|
|||
import pkg_resources
|
||||
from time import sleep
|
||||
from prototype import *
|
||||
from proto97.db import subtype_to_string
|
||||
from proto97.sensor import cancel_capture
|
||||
from proto9x.db import subtype_to_string
|
||||
from proto9x.sensor import cancel_capture
|
||||
import pwd
|
||||
|
||||
print("Starting up")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
from proto97.usb import usb
|
||||
from proto97.sensor import factory_reset
|
||||
from proto9x.usb import usb
|
||||
from proto9x.sensor import factory_reset
|
||||
|
||||
usb.open()
|
||||
factory_reset()
|
||||
|
|
|
|||
62
led-dance.py
Normal file
62
led-dance.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
|
||||
from binascii import unhexlify
|
||||
from enum import Enum
|
||||
from proto9x.flash import read_flash
|
||||
from proto9x.sensor import *
|
||||
from proto9x.tls import tls as vfs_tls
|
||||
from proto9x.usb import usb as vfs_usb
|
||||
from time import sleep
|
||||
from usb import core as usb_core
|
||||
|
||||
class VFS(Enum):
|
||||
DEV_90 = 0x0090
|
||||
DEV_97 = 0x0097
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if os.geteuid() != 0:
|
||||
raise Exception('This script needs to be executed as root')
|
||||
|
||||
usb_dev = None
|
||||
for d in VFS:
|
||||
dev = usb_core.find(idVendor=0x138a, idProduct=d.value)
|
||||
if dev:
|
||||
usb_dev = dev
|
||||
|
||||
if not usb_dev:
|
||||
raise Exception('No supported validity device found')
|
||||
|
||||
vfs_usb.open(product=usb_dev.idProduct)
|
||||
vfs_usb.send_init()
|
||||
|
||||
try:
|
||||
with open('/sys/class/dmi/id/product_name', 'r') as node:
|
||||
product_name = node.read().strip()
|
||||
with open('/sys/class/dmi/id/product_serial', 'r') as node:
|
||||
product_serial = node.read().strip()
|
||||
except:
|
||||
product_name = 'VirtualBox'
|
||||
product_serial = '0'
|
||||
|
||||
vfs_tls.set_hwkey(product_name=product_name, serial_number=product_serial)
|
||||
|
||||
# try to init TLS session from the flash
|
||||
vfs_tls.parseTlsFlash(read_flash(1, 0, 0x1000))
|
||||
vfs_tls.open()
|
||||
|
||||
for i in range(0, 10):
|
||||
glow_start_scan()
|
||||
sleep(0.05)
|
||||
glow_end_enroll()
|
||||
sleep(0.05)
|
||||
|
||||
led_script = unhexlify(
|
||||
'39ff100000ff03000001ff002000000000ffff0000ffff0000ff03000001ff00' \
|
||||
'200000000000000000ffff0000ff03000001ff002000000000ffff0000000000' \
|
||||
'0000000000000000000000000000000000000000000000000000000000000000' \
|
||||
'0000000000000000000000000000000000000000000000000000000000')
|
||||
|
||||
assert_status(tls.app(led_script))
|
||||
14
pair.py
14
pair.py
|
|
@ -1,13 +1,13 @@
|
|||
|
||||
from time import sleep
|
||||
|
||||
from proto97.usb import usb
|
||||
from proto97.tls import tls
|
||||
from proto97.flash import read_flash
|
||||
from proto97.init_flash import init_flash
|
||||
from proto97.upload_fwext import upload_fwext
|
||||
from proto97.calibrate import calibrate
|
||||
from proto97.init_db import init_db
|
||||
from proto9x.usb import usb
|
||||
from proto9x.tls import tls
|
||||
from proto9x.flash import read_flash
|
||||
from proto9x.init_flash import init_flash
|
||||
from proto9x.upload_fwext import upload_fwext
|
||||
from proto9x.calibrate import calibrate
|
||||
from proto9x.init_db import init_db
|
||||
|
||||
#usb.trace_enabled=True
|
||||
#tls.trace_enabled=True
|
||||
|
|
|
|||
119
proto97/usb.py
119
proto97/usb.py
|
|
@ -1,119 +0,0 @@
|
|||
|
||||
import usb.core as ucore
|
||||
from binascii import *
|
||||
from .util import assert_status, unhex
|
||||
from struct import unpack
|
||||
from usb.util import claim_interface, release_interface
|
||||
from queue import Queue
|
||||
from threading import Thread
|
||||
from usb.core import USBError
|
||||
|
||||
init_hardcoded=unhex('''
|
||||
06020000015cb560afa595d0dcf4fca09ebb69301e2b9e24a5dfb6f2602833427d3bd65222c418ff15b54587ab2854c4fe9aea74fa55567
|
||||
2036fa3ec73c6912c58b1b49cbdcc7664165482cd70d5bb95d9d469626fdc2e012d74157c5792260968a2a4572b1ccccc26ec2e2ed0ddda
|
||||
1b0931d3d0566460915d43e0a654a0582706be9113ea88f0c15ba058507efb0dadecb4a4cc64444dddf050b6e8d4ebb34ebba33e8651c75
|
||||
e5fb28f85c83197df1de460d9e1cb82208753ceff0ef60a823dba75d05548f5b3a5a0e2972232f7403bd6869da90e5371a0ab8ad23972f1
|
||||
597630f5ff7c8b8272800563477288b5591bbb0341d3975efc1778225767fa35480ff7f8dd633e4034ac32e4af58b86ebc63552cb35b12b
|
||||
285255deaf3a32bf46cdc5ad3bc1c9ed1bcc112c72143f9aec568e2cacfa89ba0c7bb65590d8b93e6871a33c6c6983c0acd04e737ff55ee
|
||||
e024ca6b9a48332c1a69a5a3fdd24b964cf7e7c55229bb0b48a6e339eb2c42d07ec850a4ee780660ad6c77ffa302a63bd19426134c4533d
|
||||
69192ef2e16591df26394791a4ecb994a24f5a7f70f1eb2604e6bfb67a452cb74ead8b0d9808f890ac386750cbac06ee03a852145094053
|
||||
b2b074b9905de5cdbf2272b67e51f159164778d6d2ef7a1ccb81df9f896ddb38ce11e8140cf6cb9c82
|
||||
''')
|
||||
init_hardcoded_clean_slate=unhex('''
|
||||
06020000012c40c9d271378bc0912ef5dced69bd81b7fc16972c7b46e621af54a00e2cc6baca6eb83ea30222dfc6c925262006ae93412ea
|
||||
cf482f2034ee7b13297474b7e1e91f279cac2ccb7195443e4dd3328cfd292ade073fcc2eaa8f07b77231130ba997f921b9be7b4fb6cc691
|
||||
0d2976b3e050913b27dbe73afd6e964260b9435ebab5117e71f7cb68464d4b6f8afc7e1a421f671f5854a1d0c8ab93ed3b88b2bc1a42875
|
||||
e40b15f0e78496ac40e4a4a7fd39a97534ce1866479e0384f0789bbfc2fea0ce982bf7a9df97d60b237edbe1b26c9791043a96b81e435d6
|
||||
de5971c758d374905df95b0cddabfbf531749ba191f07a6f5e2722852f137a53513a9ec6ab30c3f09aa6ce21b391e55cf81dcda6422011b
|
||||
f163317a9a4382546141d45f2274bd660103bd3af705f3ed12e493bc4f834d5d7f162e2c3405cf857b00129789a3353bf7fab7796e267e3
|
||||
062d55660dbbb857911ac8e871c460dd31c56a86a5631475f0f2ee5e9ce2af0faec0931a640ba2394025f29ffeca3a7e99c15a78ce1f1f7
|
||||
808cedd7601b9b6382d72ca873257d4f6af70e29e22afea15e36e0282b8f0bfc68ffa3417d212b8bbe11bb73b363a19872e6e947d45de30
|
||||
fbc493ca083a0a4650615d8628606362081ca6df5d67527971d1776ad76a7a28c932f0317b59cb4a82a14b2bcb7b01fb662be1496d24d91
|
||||
9140ec80068b21a818daa2fb8e05f63edbd4bd5797c74a28b3e7cf81c9045248584977711341fca3f08ba91ff853b62dc24ce4bba4ed57f
|
||||
47bd458545d805b6bb14fe0cde01440b60bf7be937f6444a8e2a10ed8fa9ddb8604bb95fe411b97112e78dbf5a4a0f004669c93765a9f38
|
||||
665cb55f5658895c1c06a7aedf694bfb3afa9b8b1dea5ab85c821ac20b0663b95023642fda36ad78e3e00140b966f404f7e55f0b416ea43
|
||||
b4c74c39900830abc6906a1004bef1b5b7dbbbeb5ec1b22604ac86429b9f56511b746a7124c449b8c9498f49144abc2d64f6a114f1d7f91
|
||||
aa41249faeef4d838e280cb5d6fc19cfe86c75f
|
||||
''')
|
||||
|
||||
class Usb():
|
||||
def __init__(self):
|
||||
self.trace_enabled = False
|
||||
self.queue = Queue(maxsize=10)
|
||||
self.quit = None
|
||||
|
||||
def purge_int_queue(self):
|
||||
try:
|
||||
while True:
|
||||
self.queue.get_nowait()
|
||||
except:
|
||||
pass
|
||||
|
||||
def open(self):
|
||||
self.dev = ucore.find(idVendor=0x138a, idProduct=0x0097)
|
||||
self.dev.default_timeout = 15000
|
||||
self.thread = Thread(target=lambda: self.int_thread())
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
|
||||
def send_init(self):
|
||||
#self.dev.set_configuration()
|
||||
|
||||
# TODO analyse responses, detect hardware type
|
||||
assert_status(self.cmd(unhexlify('01')))
|
||||
assert_status(self.cmd(unhexlify('19')))
|
||||
|
||||
# 43 -- get partition header(?) (02 -- fwext partition)
|
||||
# c28c745a in response is a FwextBuildtime = 0x5A748CC2
|
||||
rsp=self.cmd(unhexlify('4302'))
|
||||
|
||||
assert_status(self.cmd(init_hardcoded))
|
||||
|
||||
(err,), rsp = unpack('<H', rsp[:2]), rsp[2:]
|
||||
if err != 0:
|
||||
# fwext is not loaded
|
||||
print('Clean slate')
|
||||
self.cmd(init_hardcoded_clean_slate)
|
||||
|
||||
def cmd(self, out):
|
||||
self.trace('>cmd> %s' % hexlify(out).decode())
|
||||
self.dev.write(1, out)
|
||||
resp = self.dev.read(129, 100*1024)
|
||||
resp = bytes(resp)
|
||||
self.trace('<cmd< %s' % hexlify(resp).decode())
|
||||
return resp
|
||||
|
||||
def read_82(self):
|
||||
try:
|
||||
resp = self.dev.read(130, 1024*1024, timeout=10000)
|
||||
resp = bytes(resp)
|
||||
self.trace('<130< %s' % hexlify(resp).decode())
|
||||
return resp
|
||||
except Exception as e:
|
||||
self.trace('<130< Error: %s' % repr(e))
|
||||
return None
|
||||
|
||||
def int_thread(self):
|
||||
try:
|
||||
while True:
|
||||
resp = self.dev.read(131, 1024, timeout=0)
|
||||
resp = bytes(resp)
|
||||
self.trace('<int< %s' % hexlify(resp).decode())
|
||||
self.queue.put(resp)
|
||||
except USBError as e:
|
||||
self.trace('<int< Exception on interrupt thread: %s' % repr(e))
|
||||
if self.quit != None:
|
||||
self.quit(e)
|
||||
finally:
|
||||
self.trace('<int< Interrupt thread is dead')
|
||||
|
||||
|
||||
def wait_int(self):
|
||||
resp = self.queue.get()
|
||||
return resp
|
||||
|
||||
def trace(self, s):
|
||||
if self.trace_enabled:
|
||||
print(s)
|
||||
|
||||
usb=Usb()
|
||||
27
proto9x/blobs.py
Normal file
27
proto9x/blobs.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
from enum import Enum, auto
|
||||
|
||||
class Blobs(Enum):
|
||||
init_hardcoded = auto()
|
||||
init_hardcoded_clean_slate = auto()
|
||||
reset_blob = auto()
|
||||
db_write_enable = auto()
|
||||
identify_prg = auto()
|
||||
enroll_prg = auto()
|
||||
calibrate_prg = auto()
|
||||
|
||||
|
||||
def __load_blob(blob):
|
||||
from .usb import usb
|
||||
|
||||
if usb.usb_dev().idVendor == 0x138a:
|
||||
if usb.usb_dev().idProduct == 0x0090:
|
||||
from . import blobs_90 as blobs
|
||||
elif usb.usb_dev().idProduct == 0x0097:
|
||||
from . import blobs_97 as blobs
|
||||
|
||||
globals()[blob] = getattr(blobs, blob)
|
||||
return globals()[blob]
|
||||
|
||||
for p in dir(Blobs):
|
||||
if isinstance(getattr(Blobs, p), Blobs):
|
||||
globals()[p] = lambda bname=p: __load_blob(bname)
|
||||
368
proto9x/blobs_90.py
Normal file
368
proto9x/blobs_90.py
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
|
||||
from .util import unhex
|
||||
|
||||
init_hardcoded=unhex('''
|
||||
06020000013917b3dda91383b5bcac64fa4ad35dce96570a9d2d974b80926a431f9cd46248980a263c6fcef6a82839a90b59ac590848859
|
||||
afac817b7d53bf51cd3205c1b8f43048be8253c3bd247937c837aca8b18d3cc8ee8c8971ac4f688813cf3d8550d71496985b7ec07ff2dc7
|
||||
896d330fdab263a0ee433a5c4bc910439d1c6161853feb03f5502209502e7308beb7919473cfe69f422c30502d226a4d0a34d96c8c77956
|
||||
cf69db8ef6cf927a3b57849d4aa8ad4b44266923e34b82a39c8146ba3cd708c70dfedb50c2de61feb45b1d4f19584297203f5fdc865795f
|
||||
ec9d6449f3ba9b6f1e4bed698ee151e83d4d8702f76a4006cfa24d9b797888203b2269f8a77d524034ac32e4af58b86ebc63552cb35b12b
|
||||
285255deaf3a32bf46cdc5ad3bc1c9ed1bcc112c72143f9aec568e2cacfa89ba0c7bb65590d8b93e6871a33c6c6983c0acd04e737ff55ee
|
||||
e024ca6b9a48332c1a69a5a3fdd24b964cf7e7c55229bb0b48a6e339eb2c42d07ec850a4ee780660ad6c77ffa302a63bd19426134c4533d
|
||||
6f967441163fb78b73547c68a493b2f800d3cdab827b116762789992aae3c8ab345a49edd312dfd2a27bc501427dc7fa00ac3c5c36551db
|
||||
b3d5cad8d5bd7cea37e58a31307a6d50e6ae379a53f1366678c0741a3d872b8dcfefa7f63128dc8245
|
||||
''')
|
||||
|
||||
init_hardcoded_clean_slate=unhex('')
|
||||
|
||||
reset_blob=unhex('''
|
||||
0602000001d6123241376f110935a7ae38b614ced9952eaa38e2984842d0552d984b69ab5c1a70175513319970225d9ca25be063a7d70a2
|
||||
9dff803240c099ba5386910b69c3b7ca8d0b26ec9a5684fe92af9c6d8068ace0954eb8582adc6208aa6c116b1778ff531dd2b6817a10d66
|
||||
7e0385d18ac7ccb47eb67f1f54b5b94c84d65529c6e9fe9039218ae9cde123f38ffa52a953ed589d0d8bb48680aeea6544b7ceedbea3216
|
||||
615404921b43859facf8ed4448e585354c568fe8d9561efb3fc9b88af8e6c3c7c02ac9b44adbf780d23730135c1f9840b67b302b6efe9db
|
||||
19ee1f5918740dc0259b3cbdd4f10e59d1a517f54e932bf01d2b1e5ad5c4f71af23373632e06160f83799a0a1b2479d9bad01892667d136
|
||||
e27463a0eac485541f24018bd80463cea17ada264cc760b38a0ac87b27539b72e1c4f05d73f6c9c777b2e70c366f1b79d31ae2dd1eebbda
|
||||
992390c64c988c5e04e3b4ababa8c0fbaa33da18a097b609562f609ecde2443fa9b315667c95fb62abedbb8887dbcd965a1cf351c795ca4
|
||||
f5cc6e22e844cc813fcceb34741f5c988dd38d31ea3bd3cb373162efd3d5744268c1c9a5b679c97e2356c7b1de146eb675597884fda01e6
|
||||
6360b807e04c4e557ccde2a29df1030c0da4750c9314c551b6a5061711c6aa1a01564933218e2a9938d8a158a17da154969dcb3aedfb8bc
|
||||
e12024b908e7e75b9a8f44383a4a7c3a35632054ef1bffa4137e0f4d724c3a06a96fe986a339ca4f27744708feb6523a31aa4e2a19d1567
|
||||
964d52b568259cdcc602c7ebb468bc5846a9183a56917e3f440e522206e7e0e06a07aed6b680e0bc818217bb4dfb2010ff503be6d3e555e
|
||||
e1af32edb3f02f6906cdcef6053a851e799913157220c563ddcc47cf7665fede1336f152df1d1c6d145a535e59dc41b7963bb520e66dc12
|
||||
a21eb5a900f939b7838f274d9ebb7cd0c2951f2b9379b54f20bc50ee6ec2dd09d7af7b9f027261c38f056f3f1b953cf21bda1f5a9caf3b0
|
||||
7d160f80c186fc5eaa42074d041eaee81518fd2b463112ae6a6c07a185e8c24101ee14dddc4409ce7fcf9a18955bbeb8c7146dcd92ab165
|
||||
e20484c2b316acb3d9dd2215c5b7f4eb8954202628635a46b463e1b2d35c466eedc3c49951222840959db8eb1112f32fa952d72f2ce2f62
|
||||
97646ebaf53dc1c0ec484fb703938f4341f21c157fb58dc101e3918053d8abab75c9300b2912f840bacdfb4e27416bfa3a945de6cea14a6
|
||||
0f09d57e6ec7ee83f5616a9fbf0952017420f857b35fcaafdf7af50446c1e7d2624f9cc4a187af0c6ce6c118ccd4234d991402c0b81dd38
|
||||
bb523a2ecfcb16896fe16aefe7bab3ded4f746049e1d38e6f8d7130bccbac4b33bf543d9c4929b7a6247d1d4f9d5724e6040f5d4a357041
|
||||
fa07a8ad998110ca30a3ce6ac9fed580034adb66df8fa8643ad3eccfcff6ccb8df0756ff426aa9dee01d656fe699f091611c5d68688b3ee
|
||||
28e320d9ee2966a5cf146c13013fc4ce353c203689a38e0437805711141900ddfa13c5425e5c6aa5447c13a35893696ae04587901992071
|
||||
dedce8155f273581f0ba35b7b23c7b865149945be1bda2233a7b5b4e2f5530170ddd8fee44229a2bbca2622172b36831445d4c9ec9be1dd
|
||||
b14de7eab8593a6e5389cc260fb453e3062b5862ceba83dd355ab50d859bd47215c844086ef0f8d074412590eb8d1633bc21345dc1aaca9
|
||||
420fcf354ae43efcdb9a0b3c9422c6634c6e15a01d21d1cc645bb6f3e556de2c17471d4c68aca86ebc700efd31fe9eba3c31d18c33d4b94
|
||||
8441bc6f6682962018dd0704bdeb78fdfb457c70075a45397668c85ac26e13c890d1c980c2b85602f99538c649fe97a01542f112918b68c
|
||||
dc0b9e59f814e25e3dbea24c8a36b064ed7f2c924c0c45e712a9ec9339eebbe4278b1ed7fefcd7d60c27ad80a978161e36f718f3b52fd78
|
||||
37454f04b43438ea78dbe7badf356ce99e4dc3c9749d90de6d630c88e58d8f6f828759dc14d742ea4b6232efd05da4cc200732a4fab8b9b
|
||||
c8896233432333d344acbdeba7eb64634e8a91919efd81f4b848511c181f3f45ba45841cb235b47745882cdd44baa44ec8f7247c470f412
|
||||
31b2c663243068cc309aa890ef1b79f8fc438a517b903d4e35a1dbe1ecbaa2df887a1fa18af39c4cfe90104d330fa1b99ef3f1ee8276cad
|
||||
45074bfb5ec5fd72cf5b9969cbc479375bd2be271d0a8322e50f1b0c3b2c66cc0088a3e4ca6d727679833243386c441e46c25084e37621c
|
||||
4a3a6774568f65b9d2f506fe698322973ddcb94886159f55a967880f56933b066ada7fdeb861ee431369cd926ee3b088f9c5e852aed064f
|
||||
a4004fc658efacc3fec6cdc1150bd1b3183ff52cb62d598baf172126afb3d8b3eba65db880f3307078d9f263bf261bec797db705300f635
|
||||
796775d0e8d2a964f2b51ad769561b2244a48b5f9767f71da4856c329eb5a8a58ccab1993fc0f93f3491991312406c2797e5b08a0ef7172
|
||||
8e8cc3adcd23d29b7248c328b192489bec81c56be0087cf38653e4148a8065121ff7c6ad4f8aa4bcaa375f566a8c617d59abcd6c3b0aa13
|
||||
ea4590e5e37f47bc276f0f3cbfbcc0f159e4e411e3b42a5fd2c440bab916117710208ef6f113ea82709d5e0f5cdb68fc376fe707f2600db
|
||||
2f5bbef6569fbd6cec364621f30445b161d3faef69fa840c87a301604e351b6b25db6a7d20c86c1d3d4d28ebf88850f60d7ac0c1e36e1d6
|
||||
29f76f07aa65de78bc9b33324ae53312cd15d5cf54175dd0dc9b2b90583d29290a72569a5e3f1d7edec3195da0ddc547567c948b1b8bed5
|
||||
df3fcdb56f9b0c625aa288fd57815961014298da72d516ce020dc73563c0d34a7463fa869adbe24963d8f12c80d708b8f8510abd66d9047
|
||||
bd6d14db5815948fe2708ac584a6e0a6a0be66bd315ae1fb7dac0be365910ab0eeec78c9f4b4209415943ff055bce11c621628c5932060e
|
||||
b516c260d4c3da41b046a2876cc11eb59078d550bc883aa5722139a076123c4dd2debb7da792bcdaa5ace368f8f7d5c95ba0e7397393afe
|
||||
1d005cb89bba287b6ca2440295bd9c3e83a195ffcd3a853617b1d4c8b9fe1a7f5e4d4817e7e22d0d41255d044b271a653de5c6ab31b17d6
|
||||
79c5227d1fba14f5de53125424a5fafc7cedbdf560374c7d4e52d04b6fcbb725108ddc8e08e5b4fefe9ef565fb02cfaa3e56de4377c6cb8
|
||||
36cd0207bfd002522cc850380c39fb66bb92e309c26cd05c97d54ba055ad500f23c2cac8ce1fce29b4c623657a42058196c3fe635ecdb7e
|
||||
77a01a2114adac63f141169df1f3cdf1c86fb6e34fc419d686e620a427df4d9034970d6618896473b213f1933c533985f45c9197679ca03
|
||||
a6c6b9b9893492ef8a712b38d317618b6ca000bf0b5134845d6da83c9d93c3785b36ad9155cce63f74fdec88e03b69b6db32265cbf57344
|
||||
cda57e5f55d26e26576125feddaeac0141cd43dc5e906babc5162ad9c90bf623816ee21de2914c9142f32e28f489858cf931edae30e5b53
|
||||
a5e6ad8c6d74fcd0f490cd8d01904d07a8df31c6938f792319bf7d03a04b69c58b96b23d4993d10846d436c618401ff97558841489c0b7b
|
||||
1be5a8d452d4070026d4e240a9ad749b44cf8011b1aef37b789782b0f8e17460d3c43e92555c413489baf2ba1e01ca64eb58f3db61b3921
|
||||
981bdc7f005858015a987c457a9df8a35cfb70b27777e7043eb0ca06a65473ab3c503955b5a743c89d341c0a32e69047e5d9cdaca901e06
|
||||
c1b2d11fe1eb275b8d292a992a5bdfbe29d436c03fc0c85f2e5c5da3ba8a18a8b395aef869cc217afd64dd2a748d21535d89a94f64d3704
|
||||
5f0b28f73315f3d8f420d3082bc3c33a96151812ae683fcde4c9b64e96ce5c463c93c15af02649c7c56ed308c9d386216c88b1eaa87717c
|
||||
96852e80e6e544d4c3731ec34affe43bd59d1e5a3e286f23ac596c301d1311e98616b6870ae41dc3f4b62c1e8019c7d94bfbcac812a74da
|
||||
d0e12efdd0842a750c4e65e311c0b7687c180475acca570988f1399f85dec169f4674895f9ba21719b752f4b5134f036af8a6250caf0692
|
||||
8c5d4c63bab25a9f530f6f6b3b8cc4befaefac68a2b100d64a9fc90004dc2c7ffe32ebaa31ca3714a47d32b23ccd34631943870cca989a1
|
||||
1e3988ad9a884491fdaeceadd08e6eab8761d6e40b5c6bb5c6be57d879635c7899aff67d3e0ce02fcea2bcffd437434e98480a7388e86be
|
||||
96c08be01d38157a2021e6b6e7767b253e5e490d19c007d3a55f54d42d5068d574e94c252241dd0ba637e81b35255d26c4011564dc04b8e
|
||||
824c51c0635c45be6568ca3e4b8fe4b18ef259fdf0598005f153d7c880294445647c293a791ae63fa0bc7188588a4a80acf08a8d25eb376
|
||||
043a591c6dbd91e3ede155bbba2dad764ac478087e0bf5db54c1e36a751d4d1fbe04ffd5dfa60dadf6d5d4cbe1724a6bcc6376b72c0ee90
|
||||
1d2444819d4d6d368a7ec539200a66a2a85420a23cc40cdd5b6b7abcec543f0e77a2cac8f9165f17d0bcb8d64c8d17951ae64d3e3cc4054
|
||||
eb2b56265164ce17f7810f6736835e7ad6653dcb4826121ab3c3482c9c47aecd708a96875efcd744e61fd884549e09c969f78d74be12336
|
||||
f7c0a3861710dde88783aedb9975ce0ea97df46600a24dbd1580aa33ddf6cfcf845013d2b1d8f271287813c4ef56d358e6d9ce5311beabb
|
||||
9f978115ece80210c397b9bdddb0aa060c1845a8efb7b4672c8fbeccbb992f8b5a1d8cd4ba9db59f7a513368ef6ead104e3cde8a0df7898
|
||||
647b89f4f7a7d9a3a8a49e5b3467e9959e63f6ecc18fe8db37e0e3fff24685be43174a62457cac876c1bd7667671562f5989111561e76c0
|
||||
166bd50b80764861f2e893dd3c183e85f538be504e24467c633ee630aaa1162bb77beda78d8fe812cb503a3c5b9b2ae1d8d08bd8b551a93
|
||||
d1c11f9e0dadcb23032e9ddb5164a9f7f7f405b13d4c6b4348cd1aab236d2461e62c16c4be9cb7b13f227f94e588fa1b469e3f8813ef488
|
||||
b1813f99b4eea2a4bc4a01ba6a8d91efc7d49ec61414b415915515344d085369918e28320bf0827001dd044f5d2b362326eedcd1ec30893
|
||||
fccb0e7ea4d064eab96d4d948424c738af494b95ca6b581127b09c79117b4eb1fb0084084ad954b99660eb0bb1e4dfb8b3916457ecf7b00
|
||||
ae37b46e1468fe4c27368334ada48647ad511539d9a4e044cf7808bc3d9cc5cd3a6609d3f0b7c24705e6cd9558d2fc8d054102aa13c8bae
|
||||
4feb6f99854f5d51a556afaa754a4eb2825bd0b188f22dacb1921529353354fbc76e34e504815f86fa3ec7fec2ff8bcac70fed8dd8b4d35
|
||||
c5d4c40b2bbe8fb0f66a01d8250e248f91ec938fa3436d40e9bc42b5c10aaf00cda166d084210222f03c594b28c6c381431eb562dfdcd06
|
||||
4a003c230571df8d5817047d79c64a10c71b45cc35462794b4374b4f33566b0dd35aba9142c16d8b449a4845c11ccac5ace613764a2d367
|
||||
655fa0c3f61851bd78f3c1ec9be3363b55052d997286b93de7299fec7292592510acb12164538959fe0985ded8e9b3e8ad3573091f64f2a
|
||||
ba3d266e400360d9559592965e2bb090f15a79b0e18e25d5e53136cbf46f8d5c92f963b0345e4b4a953dfe62d87c8d19bf12078893f3ff7
|
||||
504b7d87e1cb22988e50bcda330944d6e21b31339c54a9f1f62f74d64e42c4ce9ea11cb28370620ddd019d72cf86f23f8d6392feba71040
|
||||
e6711f6b87d4574b3531230c2cd42811bebd42731daa3fbc22437fcbcd453d5496245d8e1d8d41a44d3388fdc5f44d5a6c2efd50aced0c3
|
||||
9182b9e13cee5afd257269a752152d2c1ad32276aedd10543d4a43c936a817f97e8504c9829a5ee7191efc6588e0fc51b68bedbc571d026
|
||||
e88e56df0cf58079ecc9f79e1da3862e197f65129cbb88cd328daa33cbdc84a10e79ec54dfb16d095d6a17661788a5b11c9b33b60180155
|
||||
a28d58e51c1bab325ec736923a5c5e68a332fccefc15e804260e0e28d2bbca5dee308429b262a1afcff57494706d7b6af55f9d1d297148b
|
||||
9cb0da0191f71a63990a69d742bcdaccca45e3b870618b87a34ab545c9fe56d8abf0595053eb34daa9d805c94cdbd7664d4b6deefacd61f
|
||||
305c6d34514d8af7e49c1fee3e61b2d5655fe1da9cd02be10a56090ecb656330f1dcc99c9dacf92db21797f8c683ea72a823f3f4916d1cf
|
||||
43a5f550e4167a6b2faadc4c76b3996e7c388e9e213356f3f55d11b65451ce82275ba73bec51220107c3aef6c16a59a8bc45c1cd18dad20
|
||||
1e967e95a0b5a1d5e79a654b8c8e8dd490d7443a3fdc3c5cc296cf161bcf7f5f11ae5e6e283b0aaacfc47e34002ab474fabd907b3b7d3fd
|
||||
e6e0d70572c72e6120688d4d2d6b527e3f2efbaf43b2c8b98acba51342d1343c2359e7e9977847941bfe4ec45960a73ac7e27f11bdd9d03
|
||||
14c4607510d9a3988504980728d94672ef5d0ced3b26318aa0d6505d27d6853c1351194b162616497a90a4811158e887f85e43c1447cd7b
|
||||
15f1cb914ef5f00756933f21781f6d50528f27e65e73063f2c9cbc31cdffd07729276ef30715c07b70203348f16fc79457f8313b41bb4bc
|
||||
c2297ce9072dd39d9cf572d77b63390df5cdc71a531470b0a03114422091bdbb9287035bc7e8883bc06515fd85d5f6a4ff6c28c0fc174bb
|
||||
f2a6deb39264e9d4d0a157f7a82160d19ca3d3320e277c67a1450597ee4ef17136908657292fb1535c54e0f33a74c2df98808dc5f0572a2
|
||||
c872f2abe3c26b61eaa51a1f431f38c1731477e56d0b7294f82450f81d3b4e24ac3b918cdafd657076dc04d0d2478c66177c52f32e63578
|
||||
ff91cf4b9c3479ff9e50d011325675b0f061f9f39a066709ec2e04799d8c6b7f5c0e6cb0f4736a9c30c2965dd1b6d130c4619c0642bf464
|
||||
779147524055003d7d106e33b8d4f2878ca1036dc26ba7c043feec62f23e5e614ed216a7688fd491141f2f0ff03901e27188c4c10ca63c9
|
||||
b16c681811946b709443fec717c0293cf956fe10f1cbc4949e90d6022e1df5187a4a9f33757b532e7afd708cd920c8cd3fb9706976ef925
|
||||
3099aea257ba89a27d015f2fad5e413fc90b1501d0bf332ae72975acb694657958ac58063b9503940bd0b5da8667d14a6f14778de42fc5b
|
||||
49aa2f31d63823b458d9d30c9cac2de6437ba07a4dc7f64c58505f8490066b8a308272afdc5f52f9d3847d45adcb4987a57e51e7f87e7b9
|
||||
7be0864cad5c32fd7d7a523b707d62fe4271b699cca423d88a7b03208615466b37e914d11bf0ac09af27bdafdb8881f594afaca9af804fb
|
||||
399357f78428a6146f4177029430ae8144efaace1396a67553501159c6b28f519fa2b6c526d0592bd769ebb8ef8d4a9cb06015a3cbbe490
|
||||
238ed5409c5c6d5c76d050afb81e794edb0cae2b5b32de7936a6dfc55bdf9629c7439ca6e0abc5a72639de5e499884f00d3893216f3b7af
|
||||
5306ea1e1c6528ffb6d216748c48e66e38195bae12b18e1dfebcdb94a769786c0d5f117692718b2e421cb6cecc31ae303a3cbf0d091aa66
|
||||
e0868531a206edc80b6b82cbdec468ee5f6995e1781fdfafeebf6e9dc29bedb852d31ab250b6114ea7914db57f2a29005c75d699f96cf67
|
||||
3c08fc8184afa71a1d1d574fdb4790ba1b903e4be7dab6759419dd5e2676e3674673517666fc2a6dff15ceb63b68898c2c04105a654ccdd
|
||||
a3004961e2fa04bbd64be6d17e072033137e9139b408e5627afce4086ef3b62ebc10d793ddae623ca685571384b72020c361a3c65737ada
|
||||
0a99626e1cc4edbf3a2d5ea7b79ee52a29399c83e3337501f11ebe19677884fc547d4856e0c056b309c9c06936b76e59179b6234dd297ad
|
||||
206a44e367938874c620d304465008cde3b9cd961fe0a2194633b60da9ffa3acdcde56ddd2e9d531c88254c1abfc82eb32d176fd6ee7ef4
|
||||
88e55ff9146e351d3f88f86458b7d1c015cbeb9a2830c94c139b88dd3722f21ffa0ed5f0fe2f52292d63e7a4e8296aaf6e7c676d50fae02
|
||||
74633b322b117a25b4ef14de3eba71c8f71c3f1d7b2b6e0a72872168d9fb4dc7736f07f74f061e9793dc11b928fbcf0640f507f258415e2
|
||||
704e590d8d8a7a3c301509a9480386e9c0dbeda03aa6ee639d321a068f1ff464de5cb8c9e1d8a18544489ebe4a10bf2a38d6b6758da979d
|
||||
d9d57cb34bf06aa039f8c28ff482f777fdf49bb0e23fd5a13997e74268bd34157b8e9759745446620b67c07a631eded4da64fd52a843252
|
||||
2a8cbef463cf342c29d824eab6dda3beae4fa865a91bb93bcf19dcbf83c30a463f2722237ec6c3219e4ea1a5c8332cd31a672816c6d6a3a
|
||||
025a2d4fe2465a70fe31e408174914901f9cd25555ad35ee447bedb46079a10e28d45dc1ddc089501cf6d2897ef5604cfb926b1f91eea87
|
||||
b3b4b5cf1de1f19de0cac590ee322b901019935650d7401f8594f07d0c5b45ff2a070c2aa99ceb579f9234d40a084bac2d327e39e4696c5
|
||||
709e8143311f8cbb1b9a9e90542f2a5c7bc31ba55fc75040ccdbb019df60f9f5be3462952c53ed5ae5d2b963e209aeec9152285b57459d9
|
||||
c30f13f0824d4df7391cd37f4c01bacca23be86522d89a3cea30d530870194c5b9f3e26854fd5c560ed8c1278727de19ecbcc98e70681f7
|
||||
f502300177bec56c7592dcaea9de7dc84e3a5bfe84518aadee912441f36a21ea72d02ad646b5627a57fa2a936f354a83de8c720bfb77500
|
||||
75652162815241de1eca2b82b26695a5be54681a352726c05f921192e2617c152ba1b3ea9886d21bdd9fa1af5ca716db253337549497552
|
||||
fa8a88d90b3aa29cc883bd6b70c3b484d61cb13a48a198d66fde544d2220853023428a472b3950a7e2454a964245e941d3afd17961e7715
|
||||
a8fcb573daf5aabad00c04f2063762b042cbddbf70e247205800cc3f683744328d36fb099de9a55f160113034f2f07d6b17bdbfca8c900d
|
||||
8ff46542836fece814f23de9c0eb1ff4d7b2ac19404aeee4ccd730479715eb68eba290f14d4e59bfffef6b32cfdccdb1887e5ac5275edee
|
||||
510d8f15c4019f8d81b5067eef86d7e0e5a22a5d9c3f9f02a3f127b790734222b35c6b58500d9e71c52af72cc857e81d674acb5a20f8899
|
||||
37b11fcd1afef357199593d4dc1ed3bd330a9884dc2ec63289aa46bc78c3ecc8bce6b233073f19838d533daecc396e4dedeaad64fe2df5b
|
||||
18ec9e5d6ffb986c2ec995e396ce4eaea867d1a37a0e37b05d62da3e33f1886fe494407c281a576656d9e877475cdde83724a74139a1a0d
|
||||
7f46c2bfe50bdf54eefc378fc5989bf3cfb8a50c767e9f387319c63ae3a19baa1f9b2ecdcd1c520cf89812f5afb7b9e1f8c4430d30ef2ad
|
||||
9ab44ec9040e004c34a98aa5365aa2c47fef4d61ae4460315695db1d9b5c209edb3bb2a19c0d3936d926de49eb38871ab49168876df13e1
|
||||
2297a0ba4735945cdfbd8e7f9213538f279f1e73962c1d93cf093c641a5e892fea0bee34981795431ec58874ca3324682e294c5919ae542
|
||||
c9a24c5517653da873a6c808f6e2ee17bf97fb096af3027ba73ff3bbe73b6666bcbf5fb343dedcca608d754253024fc1cbf745be30b9e19
|
||||
260837e7dfc018735124e321b493ae356ea682ed8b3e543f8ea78efa6fcc670a6f08b7690ef640358563dca8437c8124b54ec5c818c40fd
|
||||
0e9001ef579c8cee7ded90a0eb57f0be6a77a8bf460131ba700299188d6850e0b6bfe61ac5088cc425fe4b49de058f7b771e97f10851f3e
|
||||
f4d73f18368f206d66c46cf14d53509eeb2b0e0f7917e0424721729a624f4979f99ad06a6ebc5bfff88df86731395ee8ffdfa2f6f890450
|
||||
4ffcac2ca833db95abed61d36a611a0f2d8e911fa31e9b3d7c3b93e810298e674e1275c34789cbb4db0ad81453f79806900c7a6cf4d2bc3
|
||||
b376c3b3a85b87a96d0f391132e67beaf0efc2efe6797c8c9667b0b8a9e366747ab433056e68ceb4423b12c0e9547dd7993bb45794b3e3f
|
||||
e3ea8263af967a319496755b6dcbaba889ff3a8a63c19de97607fb0a4907f05e6c1b8751d170d5e479e22ee9aca002e3afe564129e20a35
|
||||
c6de7725d4565e354c00c0dd46a78d3bf64bee3766ab1e9713307816dca44473cf08a41db614dfe24a1f85829ec3b32d8ac299362996d89
|
||||
63d06213219dcc77057ecceb8b87238900462faa16d24138c24e9f000e40e6c2986d00d4c9e2daa5f88228921d40055c34b12a4c0a95e12
|
||||
f4261dac024a8057cfa00cc7513f7a6f7f8eec5638f561ff4ef24d7ff8e8ccacd833fd641d7de42cee3d88e92dded6db82c947b20d0e99d
|
||||
d91a4f464a3e84920a4e79b2f4bc5368c131ad2d1d36a448dd1c35e3a763a9ea77ce1ae501e5c3a3ff654d7b7f3572003aeb1eb3b9c7615
|
||||
dd1fceab65205278c43a937fe45d8ae44de67bfd69f406b55eb92f1b22bdaf71be9c40b1ce2e490ee7a3d0efe2eb8db0945c4d9f9879594
|
||||
cf15b2d2ad0e608237977a3b8109af5995a4d04a782e74acaa512c6f4124758facb7fe82b4f11ed4e1d009d4fd72c104c1f8af43bc6ee8e
|
||||
72bedbdd806066404656ff6657f6a896141e8e3c64d29462f7d9a5a8547f8e70eecd879ab3ce3a83853f686f3057796a65ddc9528e774e8
|
||||
da5a58e5b95cab51d1630c11076800c102e3a9aeddbc6393fcbf45336e380b2c243b2654abe2c60f14982174dc451e0d2cca2c2875f28de
|
||||
5dd33015d30083e4e757339ee4e8a8beea16706e0fa40505794cb6add99d4c47d3fd474c6dc6b7c12be07635f88520a396d68c1ed46cf58
|
||||
d12c5cbd71e8fd611b1814c9b6570f862376e10801b09f2a96f9fe2fb30704573552b6daf5fe225956eebf6670dfec598881774883fb71f
|
||||
1079caa7610ab316b49045282bcb95d674cb97f755db08adfb3dd48c759c1f3b846645f6e598d40d781cf90f9f6b4b68c6e4eafb04ca121
|
||||
0c3e32c9bdbbc5cc00e5cb73f6f7a8989b9f77173cf9c48e563d25c08e6421902bbf6eda178eba038a58b6314b9928e05b415274065a294
|
||||
e797d69ae030ccaf3527e5be9e7793152e34179524191d8fb034f65894c862afb9f5277355588930d06f5e1ecdd161244c5620c7927e558
|
||||
7fe7859a3c9f553a60c61dbdbb50700e94fac299688c825698738fc9057a03427491ab3cf23060d1d9e651848cf3346673ba64e252013a5
|
||||
e91fa1f208446d65120e98586e07a3f6c6b70bc10c4fdc8aaba169eb6e467d2cc12c3d5fe16b2730d12da8b30dd168f3f9174e1dc940890
|
||||
3f9b8d7dfa02cb5f5ddb014f0e4604c4920057261a4d38b62b9c0b365c6b067d10e6b71bccae65284802f3a402a0e4106bab6a2f1d3908c
|
||||
a87e8001150949e86c18d7169d0e2f96e46eca56059dfa543f121999b734561c4813e6893bcf180a0ed97dcf51c254170d991a8da792a25
|
||||
e2bb40f6e5819350cd5f19ba46634b8c7e734b6631a90f0bf7be8ac658a6ab74119c24ccc808a2ae0908722bc71a51495b232698014d9cc
|
||||
86658b7d01b5f2d3dbc402719a750c6737d0ab4d38890f361236c17d8d42a993922063a19c59a199c92310d472743f2317921246a009d6b
|
||||
8b33856583d98bcf028fed0c906e054a0fd83d3724d535f1fc7c06e03c504aa1d5254ea9d100658ba42771aa41717a7666c11b7cfcc21db
|
||||
52b598f9754621372107f06f435d01f624b258f9d987a8e3c4699fc1c8eaefbbb2c6bfa52c24f2e7fa61a8894a598c6eca124978b566131
|
||||
cb23a1ea29d5a877156726954b4e025e9cdc6615baf34af6192c5ee9f23fc48c8ca9cbf66290e52c93503925b8df6718fc9820c84480bf1
|
||||
50cd0679c0042b52ad5dd97d3cbf59ccce7ffc168bfd49de2f00034f9c2d16f6c438e6c1d156186ff15950a1b829abcbb6c339f443cd23e
|
||||
6d0be60e818fa0571c1cfc22d11f60423f54f884e4939d7b742a0af3a14531d9341e4e88c98f800ee5d05c9ff29daae165db0aa48e4ad79
|
||||
25e25ee0acf03bf09393c5ccbbd62343656bedb3a68d7826b01bf9deeebeb32e32058853923f235095cbf5fe9099185ef9601b1e59e96cb
|
||||
e812a8ee818a5ae1135de863f5241fbccd6277ce0261487ac5925c8a6d303666a045665385b3177e4f4945bac6fe4ba1e3bfa407b964b8d
|
||||
02f5dd122e1806ebd8c5f92a260b635248d6fbd993c03ae92f75c16a14ce240b1742ea5d0c25ef1629ab0c6bb59ba849cd7a0c15284a780
|
||||
0ae3b61b13becbed1337d37987e36ebd9b75a8e64754a8109b5efce08306244aa62cf7d0ff73b73f2eaa363d6cfb993be1196ecb43edf51
|
||||
e702e7575fc3940a36c2075223a7ea6c06466f8d689d6a8f9b2309d893073ad6ad0bb44044b1bc7d0c093b049c07772e39d9e4717733887
|
||||
157460332241e807e07b0a848715ae8eb085eb96116d1e5e3e98b70321af069c52d34f5e8f99198d18c0a7e0e702fe4a1241956b09ab6b9
|
||||
e5040ba2a9f0d9c5fe87eae988913c5e08554d32feeb3e60148b1fb1ea67b2ff55285e928efe9039197fe9783eb0ec13d28aa74fcf9e554
|
||||
9b48ab7098927df50a6cb1bad0dedf8236cb099c1746855b180be40154aca88b4e966cb89ca2813e57dcb0fb7d36f4fbab5ad82a98522df
|
||||
ddd9e50b9f6335c97b7d36cf8f082d4efdf887cf09dfa42a4926bbb1b58d033c6ad34373045e0e2c9ea83a537127c3c7b2c99b53187623a
|
||||
a5b429c6ce47499adecdeb536ebe3d884d4109bc06de2f54c221258c15d658ea5dd02039d44af44ca6541e6f4eb7242ed1973ddb5cb0d08
|
||||
aa78db6ac14d7efd2b20db298ad9bc1cd6c8b3620c8f035496ca386450a81b73022bb9d6bdf7b5142ea8e49e923a6515404f166c24d1e01
|
||||
3b1b040759e083212dd9f2dd0b9bcd7d66f2eaae71b4da533dbaad18f34c86e72d0b836d8a78c0ab0e9fe657616c67c021b99313f0661ff
|
||||
e4628a9d3caf75c54727c1720ac71bb484fae167b24d2c197a07cfa10343dbebb02a23e45c7e6cea8f8fe242a3d7407cbcb1553d05a643c
|
||||
609c9783b43581b957e473569c3b2cf88c1ef91eb4a62392b20a15297f8c85cd7a1cc20b794abae302e3cd3ca869b4dc2258c57ea2849e8
|
||||
1f31ff2961ef1f02bd7170677848c1d2aa5f4b854682148e651f34bb90fbad9606bf1b096f47c0a662d03869ab963308b7454d921bcf04c
|
||||
35272d5684b31c6182a4b446eb991a92ffe783b497aa09bc969169a843e795c577c94ddfb2781bdea0f23bcb5b98c12cac376085219ec53
|
||||
54bfb1986bf9e8e2369f4dc1cc59f0e4971d2710037e35cfa228482fbc8fc197fc8a483937e72c54728efbb7e26b3b7122143e62e0f96c6
|
||||
bf7a5673444f55af3a224f980ece97cb3152ea3a36a45a47a8190d255e8f8983aa353b99d8441969b5ba7f8c2130766052b7d9aa4962d73
|
||||
2d618b561b38ba4a7d5851cb1baa6d032d4078114f5e943bc53af0eb52e422361acd6e088c98a477000ae91b11242f083554a921837b69f
|
||||
be571ee7460e8877a74d50f02d3c8061591c73591cc67f13a4c5ee8ee00211a4142408a7530c0de5c606d7d76479bec4d9c0ed8d96a1274
|
||||
b589488fe0e73c8be1f69b1a5650ffda224c263544357b22561f64ec9e3b82525022af207610073d6b26ecb3a3f534c95c5bb0536eecafa
|
||||
f7d83586e88e7174becc82eeec7d8ab52f86caf55fb12d6e08d29242a38cc7b4b94a6219e463426e5a4b38a9f33932c1da2de02ac96a5fb
|
||||
a25002790ab95cfe61d720dc29cbac8adca48da51bbd22cb79a81fec9e9a3af21e7d22581c4ffb7375920ba798871a0cb69ce6c98fcec09
|
||||
b4520d27d4d062c151865704ca63de90b733c7b49b6a94ba5477e2c585a1e81fffc5d491d85027a9cb77334988a915e5a79917607a00354
|
||||
f1ece6334d9f3ecffb5b8c10f1d059c2f63baad95c239a9546553045cf3c9f318d7169d24db36ce3b088609e766882040011b7bc05bd271
|
||||
b67bf8b372fbc750bc3edd134d3c59e87ee13abb4c2ae32f66b42c5b48ac382102c7039619a94f8bab8ff56ed5fec6b44cec3ebdf74c7c1
|
||||
862b334cab340202d1a86b56b4981e25d83a33f5bcbc48b260c0b0f092f133c65dd1dd6450c0ae5c04d4ed44b16237f0618e4d810dfccab
|
||||
1e884693e683a46c79b76e7028f0a12ffca9115df65e7e7d5249cc85eeb1d5b5a736f47226895c0445ddd714eaa636ef4d866b54b7f7187
|
||||
38f539c063f6e84ed8255390e410b2400ae390a907b2d428de5c10f78e499d8f433f1d3ead0872f6f8469b35baa4c702a7b69ea9a6e657d
|
||||
6422c00f300bc15cdfa9a41eab53096bdf3dcfb2d80ed70219818cd697bf03759aa888bcee86a01741e316cd28441207c12fc9b36ee8cbd
|
||||
c2d774656b58441799fa05155c1614c4b735c081c5af2e18d9ad8abc733d2924fb8d6ce8b733d56d67d9482aa49bac974e7de109bf6d32c
|
||||
924e96172455c4bcb0aa85b6812c704a2af87c683a1f2a9a9101d0fe0ca70a6a6d5ca1fff045b823235c334536b9c429da3272327be745e
|
||||
fd912040a6445c245f5afa146350f9f193651f8db41ae4d4afa7ab81198043be7cf59dc6de0283f27fe848cbdb2962cacfb96803d9e7ebb
|
||||
497ab1b0e65f802bdd27b9a36333ee24ae7f5e94a8cfadde40e17b01d6c72d1463f68ef98169eec313f97a02e0634cbe89e2dd4c4d53217
|
||||
fab8bbece82b1ce9dff25ccc6f7c86f8e743e269492aa5cd7fae7b0ec5ff323b53d70ecf0bd8529898531c6b85f42a304b152bccfae92ba
|
||||
ed9732d68584c507b510cb27c73277388eea9567b7c3b1f7611d2e2562a86930281e815c0919e87c9ab923cd8be7175b7dbe8413e37fc51
|
||||
2e0d4a0a8d47ac53df1e19576369539f7b5d74be7f93a2a866a041fe3a8d23196057099afdcb41353c6eba769edae13e502c8a33310f700
|
||||
9faf5b858b9fda982c3ccd28db2c9cecb7022d981c47967bafb8cb4be54f0d426d5ec7e414e3ea0eb9a0e4218ced00ceb393f4ebeade224
|
||||
df16af55305155ab2af8fc8a983ba64faee53aaf1cb6e5a1794d3fd0506e0685e4b002d69148aaa795a28d3106edd9a6a001ead88c221ac
|
||||
539cc44a8a282455c25804a6c26ddabbeb0c917e835a1f71acf6920a33c2732d75465077cda205ae87c9d23d8b8f67d565881ce3b4c48b6
|
||||
e06d93e310fb1f2fa0085493682ee124d582d85361e29c0cdc2f68e93a124671d9e70c4328793406c5622864dd903c7a5a0244cd24d4a8b
|
||||
61f4d1647d524e5de8acd571219455d57438b0907a230ed3e47a9bd3f291548aa15a8006308e7f1a8593e27f9fa55ef280266a3f1016212
|
||||
db2921e80ae9c5d838a24071ac873fe8245e2e877d466f69eb7c87dd9176f31d38476ebfa9f287a699b7d4634902d22b843c575714c84ab
|
||||
f02f04edf7f56d7646df65c155933b643dca2a2e7621660635e215401ee7004ded4aa9e023b13bf1cdf44720218a02975b9c14707a5baf2
|
||||
25bfc4937ead5193d533ec78246de1dd94a23f56ad47de9d5b6a47ce60af33324f5afdf1981c2b460da884ecbe4a7f082527fceba6dfbdf
|
||||
a5dd32d7cbc08a0f4f24b8411821c0b22e1053029dfb9b0d0e61c997f9b4efd9783c8c384cb72a042c1ff23d3eb802a367502ff73e952ec
|
||||
cfd9b6fa422193defc9f040df291ee198b931fe376b5a4acc56cae472c734b08a4eba293503bd534a14e935b925322d5bb083cfd2ec1600
|
||||
893ace9e0772339d17f724f4e54f1d9a998fa93dcef407ae4bfc144938a7cacbbf506327b6ef761f6b8bcda139b2852b5d10f325effb71c
|
||||
bb19425c6a212261604b3ab229fa991d8c0011fc3bc3592d0bee2243db7485ab0fba3520538ab9f527cd5a1dab5205de3261c8283c93a9b
|
||||
6aaf1e66c32b7a445526cffc142ddeff1c172c583def9bdc2d9f73882e65b9cc7490739a064c62e04f4ddba4957badd6b50c64a13d8790e
|
||||
2820dd20d6c1218462af6a070c768e8c953b779c732de4ff756f87ecf2695caed49461216883bf5539415359691a57e69b08c0630fb2732
|
||||
eeee2d6e6d99df1cbffc2212e79c8cd2ab4f8052acf0bd2bdea1d3337cf5b1773f5a1a095e431ed7e467fb386dbab83d30b4924f6bb4d9e
|
||||
f0851f3cd218c48441af9520e2bdc12db9e4d3b22ca685e9547d2a24a5b9aef06edb4944fde879a8ca7fd2522fca7591017e16881637d12
|
||||
01f13547b777fee849eec6cd95d2bb18859b5367e1ea78728bc7a7cdc8deb7b92103f65fd799d23dc7e31adbe62b729dd222fbf3e15a97e
|
||||
f72aea07e183577f591a081845ea340797b79c6c8742e8826a6d7df773ec8564025fbb570bfbc001740e3437384df6324c7605526d9a630
|
||||
a5d4483f1
|
||||
''')
|
||||
|
||||
db_write_enable=unhex('''
|
||||
06020000015ddf5d76a8e3bab8b10d9f82f0b31652d5b208dff1d20745b7c5442e09db880c80ca103d5b2071d8bdcaab452b900d1785be2
|
||||
de478669e72a9d082273f0f6176b700ffb65a026af052e76de5f9e247e994bed7c79e34815b465a8070a76da7d365525d7285dfb152d24c
|
||||
732a471f031d22ebb67d6e27bfff74b3ab1c4780461a460c0ea5710fbd32ab59a6ca9ca3e3e24264640b15245a6c48b6bd20c6848c2307e
|
||||
854af75b059e86793dc7a30474a1b1b5484c58dc5a4535eb71911bf9b10f82c27362d68b4baa1ace63a33b7cf3de616810ee5033601febb
|
||||
cc08b0f55166f4ca08d42d3361426712f7e5a4842e9d2e906c87e4c5c6694eec448c109abe3238128fbbb8b8f383e3b66133f0c9d38ca53
|
||||
5d47ee9dd8841fcb205510452bd0f733d416b6c763f0b7a647e1992ede714b6c4569de329440549a5a1f6646dc88b08288fc6f9c4f55d90
|
||||
ccc86487af195ed635e4c9a5a2df5df3bc4e39ff0ad6bc70a54d142a01ba9ef22839859ea1cb9b227cf9407a9f61b3cf8273549d759c3ff
|
||||
b6db4b33bf9a40f6de2680cde8ad01158ed1c6771bba9fbe230c9e9b8a4edff941f43c4e629097c6149a5ac13ebe757b43a52af3f263f8a
|
||||
a36ec69989c62539b656a49dfdb14ea066ffec203c41411de29251bc40f75860976834acf368db991d0c88cc5d9f8229be691f55c1dc298
|
||||
dd0690aa09f46a7a0dcb92909a654a54cb584ed65280236bf368ab2ae909086dcbafa6fbe26556cd724ee9e6b7b3ce2290c5cb832bd4301
|
||||
66047ab2281c55e7d9d29637bbadfa40b4b9a5008e86239a5998d39a5dda6cfeb7d0bff10bf56c523878db6de81b7ed824fdfdd9515f5d4
|
||||
90345b47172bdc9db6bbb10f19d39f061fbe53c3397dcb8fe30d5ca032ccf2d26184635b691705b471d81dc86ee31f144512072d9ef7559
|
||||
e9ef9db8a08fea98abb6706833a9684855578ec04b72ae1bc4e559c87dc1dcbac3115c2c6697cdb45762402836580f29330cb0fe8e4116b
|
||||
dcd4da603082ca420f5de71b92657c5a6ca96f51e6ed9945ffd39e9c79441ba5cba40b2c09af4d16c3d7957a2d8ab7469819c0c6dd177e3
|
||||
e7dea2240a1d96756b5fde207543167417a74e702df1781f65165c4ed7bc993e72789af03c2cdcdaa7b558423fd064328d57f2ec9a8ab2b
|
||||
ca9bda6f625e103a79a12561675e05b448f5dec7554649fb10ee3d28599306de19f63ce50e4c346cabff9f5ccd36072831664c308e63184
|
||||
cbf31e5541f46e01500d6ed045d7d44eb8087355c5bd5a65c58c705b5299e52cebcbc79254d20b6835c76cfd47d99f9b1f4a96169249a5e
|
||||
28d6ca753efdb8a008edffe2a7f68ae23bb7d6f84f40751f0232251d4ad4dedf0fb4b8b5f8fc4701bc4578cd8e1c4bb491d16196e952e0c
|
||||
e684e63ce9b608ef61551011328866545a6999e0881ffc758e41b451e1c2c8f571294eb1d5828131af9735003d00488200dc3b9f6cb6a7d
|
||||
2bc1239367b372914d4a3a3ecafc89372497d9621c1629c1aeb40d9b6f17efe1fdf19583ae2867efaf0c682d59ef72397c7d709da80425c
|
||||
30b91b1716893039205b9dc46ef8979aa5f8004c6bb698b35c4b600b45cd9dfc2a929fec37d392ce9e3b83240707226402d89bb034c3a23
|
||||
07413ba39eb1afdcabbabe9742fe2c62acb679a57ee903de0a788fee19d550b78c4f2095e388766724061b39a1e54c585ddbce238a385e5
|
||||
0c4ba9866c16ea8f438cc58aedacd6c2652b0ab0847794f80968daaf8a70422c28545caf3e95c38c54e142c8ebf26d5b5adcf905d8f105d
|
||||
ef1df70997ccaf59e256e7b178e4bc9ee8d6437f23f59e7dcc8fb3f18769fa67bba0b4791cf8c0a4e330751e671be3235a9ffd83272eceb
|
||||
af2d3b91a91599b1d4f84aca5660dca24acfe5fa78e95f944b4930db9e81a6f3ed23c90b2a446ad7832b6f4bd8e6b932116f4aedab500a4
|
||||
dfe311d54b3ff9ddd3c73819b5c88a47c87a117c3a008e49361ba1ad4632d38ee4f2fd8822c079a278b491ed5b9488cdfff5ea48dbf8aee
|
||||
793a2d2e837a9f5fd195833c104da32c3a1363ac846d2a59ffca0c3b1293415069db5e459edf60d704de47138c315df3a8440b378ee88e1
|
||||
cdb8889b08e0cbeb4ed98311e5218369535e03062507d7c1d13b522e3eec020ffd4cb1b2297760adb4ef7c76a5fd2e78a60bee3cf246f24
|
||||
8fad7fe6fa8e9c65b52515a7ce73c47c20c04c32f377e1d0ddc20e7e96f7fea55fd85d8456849ffe4cc3c6a03c5b9f21d055e4d498934be
|
||||
ff14deec1e77f857004d929409e23e54eb19e72a733a3fb3abd035b72a4a4c2076874061baa5526e855be24b413758cdcbe1150bc916514
|
||||
254ecb95848fc717718265a6901966f22c33dd6ae48ff9cb5731446addb3ff2ea17b1c3586e52afa23bb44ea6c2bec062c59adcb791736a
|
||||
a2da95eb8445a6381f3941917bed7fcf01ab199228eadd1968847e4b9a40022d2b0ec68318abf97c6d46286a5
|
||||
''')
|
||||
|
||||
# FIXME this must be very specific to a particular device and constructed on the fly from multiple hardcoded tables:
|
||||
identify_prg=unhex('''
|
||||
02980000002300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c210
|
||||
000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902a020
|
||||
0b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc2
|
||||
0e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b0000080400140800000808000008080000140830000808000014
|
||||
0831001c081a0032000c0000000080501101004c1126003400080310071d10071d10071d10071d10071c01065810080101000007c8078c0
|
||||
6100000204f80007f000003070107010c07032c08fc80095a800afc08fb800b5a095b800afb08fa800b5b095c800afa08f9800b5c095d80
|
||||
0af908f8800b5d095e800af808f7800b5e095f800af708f6800b5f0960800af608f5800b600961800af508f4800b610962800af408f3800
|
||||
b620963800af308f2800b630964800af208f1800b640965800af108f0800b650966800af008ef800b660967800aef08ee800b670968800a
|
||||
ee08ed800b68096c800aed08ec800b6c096d800aec08eb800b6d096e800aeb08ea800b6e096f800aea08e9800b6f0970800ae908e8800b7
|
||||
00971800ae808e7800b710972800ae708e6800b720973800ae608e5800b730974800ae508e4800b740975800ae408e3800b750976800ae3
|
||||
08e2800b760977800ae208e1800b770978800ae108e0800b780979800ae008df800b79097a800adf08de800b7a097b800ade08dd800b7b0
|
||||
97c800add08dc800b7c097d800adc08db800b7d097e800adb08da800b7e097f800ada08d9800b7f0980800ad908d8800b800981800ad808
|
||||
d7800b810982800ad708d6800b820983800ad608d5800b830984800ad508d4800b840985800ad408d3800b850986800ad308d2800b86098
|
||||
7800ad208d1800b870988800ad108d0800b880989800ad008cf800b89098a800acf08ce800b8a098b800ace08cd800b8b098c800acd08cc
|
||||
800b8c098d800acc08cb800b8d098e800acb08ca800b8e098f800aca08c9800b8f0990800ac908c8800b900991800ac808c7800b9109928
|
||||
00ac708c6800b920993800ac608c5800b930994800ac508c4800b940995800ac408c3800b950996800ac308c2800b960997800ac208c180
|
||||
0b970998800ac108c0800b980999800ac008bf800b99099a800abf08be800b9a099b800abe08bd800b9b099c800abd08bc800b9c099d800
|
||||
abc08bb800b9d099e800abb08ba800b9e099f800aba08b9800b9f09a0800ab908b8800ba00801800ab808b7800a010802800ab708b6800a
|
||||
020803800ab608b5800a030804802003070404020000000000002f000400900000002900040000000000350004001000000017000000260
|
||||
02800fbb20f00f2220f00300000006001020040010a00018000000a0200000b19000050c360ea010910002e001c00020018002300000090
|
||||
0090004d01000090017c013c323232640a02013000cc0103000000ff0000001d000003ff00000025000003ff00000022000003101112131
|
||||
415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b
|
||||
4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081828
|
||||
38485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f
|
||||
201d1c321a301e1c211e21341f1e202024201f1e20201f212221221d221e23341e1d1e1d20341f1d193b341c1d1e35201e201c20221f341
|
||||
c1e1e1c221f201d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1f1e1e341c321e3220301d2d302f2d2c
|
||||
2b23223a211c7e7f807f8080808080808080808080808080808080808180818081808180808080808180818080808180818081808180818
|
||||
08180818081808180808081808180808081807f808081808080818081808180808081808180818081808180818080808180818081808180
|
||||
81808180818081808180818081808180818081808080808080808080807f807f807f807f7f7e7e
|
||||
''')
|
||||
|
||||
# The following blob has only 1 byte difference from the above
|
||||
enroll_prg=unhex('''
|
||||
02980000002300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c210
|
||||
000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902a020
|
||||
0b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc2
|
||||
0e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b0000080400140800000808000008080000140830000808000014
|
||||
0831001c081a0032000c0000000080501101004c1126003400080310071d10071d10071d10071d10071c01065810080101000007c8078c0
|
||||
6100000204f80007f000003070107010c07032c08fc80095a800afc08fb800b5a095b800afb08fa800b5b095c800afa08f9800b5c095d80
|
||||
0af908f8800b5d095e800af808f7800b5e095f800af708f6800b5f0960800af608f5800b600961800af508f4800b610962800af408f3800
|
||||
b620963800af308f2800b630964800af208f1800b640965800af108f0800b650966800af008ef800b660967800aef08ee800b670968800a
|
||||
ee08ed800b68096c800aed08ec800b6c096d800aec08eb800b6d096e800aeb08ea800b6e096f800aea08e9800b6f0970800ae908e8800b7
|
||||
00971800ae808e7800b710972800ae708e6800b720973800ae608e5800b730974800ae508e4800b740975800ae408e3800b750976800ae3
|
||||
08e2800b760977800ae208e1800b770978800ae108e0800b780979800ae008df800b79097a800adf08de800b7a097b800ade08dd800b7b0
|
||||
97c800add08dc800b7c097d800adc08db800b7d097e800adb08da800b7e097f800ada08d9800b7f0980800ad908d8800b800981800ad808
|
||||
d7800b810982800ad708d6800b820983800ad608d5800b830984800ad508d4800b840985800ad408d3800b850986800ad308d2800b86098
|
||||
7800ad208d1800b870988800ad108d0800b880989800ad008cf800b89098a800acf08ce800b8a098b800ace08cd800b8b098c800acd08cc
|
||||
800b8c098d800acc08cb800b8d098e800acb08ca800b8e098f800aca08c9800b8f0990800ac908c8800b900991800ac808c7800b9109928
|
||||
00ac708c6800b920993800ac608c5800b930994800ac508c4800b940995800ac408c3800b950996800ac308c2800b960997800ac208c180
|
||||
0b970998800ac108c0800b980999800ac008bf800b99099a800abf08be800b9a099b800abe08bd800b9b099c800abd08bc800b9c099d800
|
||||
abc08bb800b9d099e800abb08ba800b9e099f800aba08b9800b9f09a0800ab908b8800ba00801800ab808b7800a010802800ab708b6800a
|
||||
020803800ab608b5800a030804802003070404020000000000002f000400900000002900040000000000350004001000000017000000260
|
||||
02800fbb20f00f2220f00300000006001020040010a00018000000a0200000b19000050c360ea010910002e001c00020018002300000090
|
||||
0090004d01000090017c013c323232640a02013000cc0103000000ff0000001d000003ff00000025000003ff00000022000003101112131
|
||||
415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b
|
||||
4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081828
|
||||
38485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f
|
||||
201d1c321a301e1c211e21341f1e202024201f1e20201f212221221d221e23341e1d1e1d20341f1d193b341c1d1e35201e201c20221f341
|
||||
c1e1e1c221f201d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1f1e1e341c321e3220301d2d302f2d2c
|
||||
2b23223a211c7e7f807f8080808080808080808080808080808080808180818081808180808080808180818080808180818081808180818
|
||||
08180818081808180808081808180808081807f808081808080818081808180808081808180818081808180818080808180818081808180
|
||||
81808180818081808180818081808180818081808080808080808080807f807f807f807f7f7e7e
|
||||
''')
|
||||
|
||||
calibrate_prg=unhex('''
|
||||
0298006103230000002000080000200080000001003200700000000080202005002420000050207736282
|
||||
0010030200100082170000c210000482102004c210000582000005c20000060200000682005006c200129
|
||||
70200121742001887820018084202000942001809c200902a0200b19b4200000b8203b04bc201400c0200
|
||||
200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0
|
||||
206401e420d002e8200001f0200500f8200500fc200000b8203b000008040014080000080800000808000
|
||||
01408300008080000140831001c081a0032000c0000000080501101004c1126003400080310071d10071d
|
||||
10071d10071d10071c01065810080101000007c8078c06100000204f80007f000003070107010c07032c0
|
||||
8fc80095a800afc08fb800b5a095b800afb08fa800b5b095c800afa08f9800b5c095d800af908f8800b5d
|
||||
095e800af808f7800b5e095f800af708f6800b5f0960800af608f5800b600961800af508f4800b6109628
|
||||
00af408f3800b620963800af308f2800b630964800af208f1800b640965800af108f0800b650966800af0
|
||||
08ef800b660967800aef08ee800b670968800aee08ed800b68096c800aed08ec800b6c096d800aec08eb8
|
||||
00b6d096e800aeb08ea800b6e096f800aea08e9800b6f0970800ae908e8800b700971800ae808e7800b71
|
||||
0972800ae708e6800b720973800ae608e5800b730974800ae508e4800b740975800ae408e3800b7509768
|
||||
00ae308e2800b760977800ae208e1800b770978800ae108e0800b780979800ae008df800b79097a800adf
|
||||
08de800b7a097b800ade08dd800b7b097c800add08dc800b7c097d800adc08db800b7d097e800adb08da8
|
||||
00b7e097f800ada08d9800b7f0980800ad908d8800b800981800ad808d7800b810982800ad708d6800b82
|
||||
0983800ad608d5800b830984800ad508d4800b840985800ad408d3800b850986800ad308d2800b8609878
|
||||
00ad208d1800b870988800ad108d0800b880989800ad008cf800b89098a800acf08ce800b8a098b800ace
|
||||
08cd800b8b098c800acd08cc800b8c098d800acc08cb800b8d098e800acb08ca800b8e098f800aca08c98
|
||||
00b8f0990800ac908c8800b900991800ac808c7800b910992800ac708c6800b920993800ac608c5800b93
|
||||
0994800ac508c4800b940995800ac408c3800b950996800ac308c2800b960997800ac208c1800b9709988
|
||||
00ac108c0800b980999800ac008bf800b99099a800abf08be800b9a099b800abe08bd800b9b099c800abd
|
||||
08bc800b9c099d800abc08bb800b9d099e800abb08ba800b9e099f800aba08b9800b9f09a0800ab908b88
|
||||
00ba00801800ab808b7800a010802800ab708b6800a020803800ab608b5800a0308048020030704040200
|
||||
00000000002f0004009000000029000400000000003500040010000000170000003000cc0103000000ff0
|
||||
000001d000003ff00000025000003ff00000022000003101112131415161718191a1b1c1d1e1f20212223
|
||||
2425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4
|
||||
e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778
|
||||
797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f2b23203
|
||||
c2d182e1e30182e1c321d341d341e321c301e1e241e201f201d1c321a301e1c211e21341f1e202024201f
|
||||
1e20201f212221221d221e23341e1d1e1d20341f1d193b341c1d1e35201e201c20221f341c1e1e1c221f2
|
||||
01d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1f1e1e341c321e3220
|
||||
301d2d302f2d2c2b23223a211c7e7f807f808080808080808080808080808080808080818081808180818
|
||||
080808080818081808080818081808180818081808180818081808180808081808180808081807f808081
|
||||
8080808180818081808080818081808180818081808180808081808180818081808180818081808180818
|
||||
0818081808180818081808080808080808080807f807f807f807f7f7e7e
|
||||
''')
|
||||
|
||||
|
|
@ -1,6 +1,35 @@
|
|||
|
||||
from .util import unhex
|
||||
|
||||
init_hardcoded=unhex('''
|
||||
06020000015cb560afa595d0dcf4fca09ebb69301e2b9e24a5dfb6f2602833427d3bd65222c418ff15b54587ab2854c4fe9aea74fa55567
|
||||
2036fa3ec73c6912c58b1b49cbdcc7664165482cd70d5bb95d9d469626fdc2e012d74157c5792260968a2a4572b1ccccc26ec2e2ed0ddda
|
||||
1b0931d3d0566460915d43e0a654a0582706be9113ea88f0c15ba058507efb0dadecb4a4cc64444dddf050b6e8d4ebb34ebba33e8651c75
|
||||
e5fb28f85c83197df1de460d9e1cb82208753ceff0ef60a823dba75d05548f5b3a5a0e2972232f7403bd6869da90e5371a0ab8ad23972f1
|
||||
597630f5ff7c8b8272800563477288b5591bbb0341d3975efc1778225767fa35480ff7f8dd633e4034ac32e4af58b86ebc63552cb35b12b
|
||||
285255deaf3a32bf46cdc5ad3bc1c9ed1bcc112c72143f9aec568e2cacfa89ba0c7bb65590d8b93e6871a33c6c6983c0acd04e737ff55ee
|
||||
e024ca6b9a48332c1a69a5a3fdd24b964cf7e7c55229bb0b48a6e339eb2c42d07ec850a4ee780660ad6c77ffa302a63bd19426134c4533d
|
||||
69192ef2e16591df26394791a4ecb994a24f5a7f70f1eb2604e6bfb67a452cb74ead8b0d9808f890ac386750cbac06ee03a852145094053
|
||||
b2b074b9905de5cdbf2272b67e51f159164778d6d2ef7a1ccb81df9f896ddb38ce11e8140cf6cb9c82
|
||||
''')
|
||||
|
||||
init_hardcoded_clean_slate=unhex('''
|
||||
06020000012c40c9d271378bc0912ef5dced69bd81b7fc16972c7b46e621af54a00e2cc6baca6eb83ea30222dfc6c925262006ae93412ea
|
||||
cf482f2034ee7b13297474b7e1e91f279cac2ccb7195443e4dd3328cfd292ade073fcc2eaa8f07b77231130ba997f921b9be7b4fb6cc691
|
||||
0d2976b3e050913b27dbe73afd6e964260b9435ebab5117e71f7cb68464d4b6f8afc7e1a421f671f5854a1d0c8ab93ed3b88b2bc1a42875
|
||||
e40b15f0e78496ac40e4a4a7fd39a97534ce1866479e0384f0789bbfc2fea0ce982bf7a9df97d60b237edbe1b26c9791043a96b81e435d6
|
||||
de5971c758d374905df95b0cddabfbf531749ba191f07a6f5e2722852f137a53513a9ec6ab30c3f09aa6ce21b391e55cf81dcda6422011b
|
||||
f163317a9a4382546141d45f2274bd660103bd3af705f3ed12e493bc4f834d5d7f162e2c3405cf857b00129789a3353bf7fab7796e267e3
|
||||
062d55660dbbb857911ac8e871c460dd31c56a86a5631475f0f2ee5e9ce2af0faec0931a640ba2394025f29ffeca3a7e99c15a78ce1f1f7
|
||||
808cedd7601b9b6382d72ca873257d4f6af70e29e22afea15e36e0282b8f0bfc68ffa3417d212b8bbe11bb73b363a19872e6e947d45de30
|
||||
fbc493ca083a0a4650615d8628606362081ca6df5d67527971d1776ad76a7a28c932f0317b59cb4a82a14b2bcb7b01fb662be1496d24d91
|
||||
9140ec80068b21a818daa2fb8e05f63edbd4bd5797c74a28b3e7cf81c9045248584977711341fca3f08ba91ff853b62dc24ce4bba4ed57f
|
||||
47bd458545d805b6bb14fe0cde01440b60bf7be937f6444a8e2a10ed8fa9ddb8604bb95fe411b97112e78dbf5a4a0f004669c93765a9f38
|
||||
665cb55f5658895c1c06a7aedf694bfb3afa9b8b1dea5ab85c821ac20b0663b95023642fda36ad78e3e00140b966f404f7e55f0b416ea43
|
||||
b4c74c39900830abc6906a1004bef1b5b7dbbbeb5ec1b22604ac86429b9f56511b746a7124c449b8c9498f49144abc2d64f6a114f1d7f91
|
||||
aa41249faeef4d838e280cb5d6fc19cfe86c75f
|
||||
''')
|
||||
|
||||
reset_blob=unhex('''
|
||||
06020000013920c0cdd8e7e68d6ef897ee686fbf657f83b9514341e1d3c0835a28c9cd1ccd0016644f74d
|
||||
d02a73b85d95f4d352417cee76ba0bca3ce474159c62ade3e10797715a13f15a292f5d57c6d46cf8f5ef6
|
||||
|
|
@ -37,7 +37,7 @@ def persist_calib_data(calib_data):
|
|||
|
||||
write_flash_all(6, 0, calib_data)
|
||||
|
||||
def calibrate():
|
||||
def calibrate(calib_data_path='calib-data.bin'):
|
||||
# no idea what this is:
|
||||
write_hw_reg32(0x8000205c, 7)
|
||||
if read_hw_reg32(0x80002080) != 2:
|
||||
|
|
@ -53,8 +53,8 @@ def calibrate():
|
|||
|
||||
print('FWExt version %d.%d (%s), %d modules' % (fwi.major, fwi.minor, ctime(fwi.buildtime), len(fwi.modules)))
|
||||
|
||||
if isfile('calib-data.bin'):
|
||||
with open('calib-data.bin', 'rb') as f:
|
||||
if isfile(calib_data_path):
|
||||
with open(calib_data_path, 'rb') as f:
|
||||
calib_data=f.read()
|
||||
print('Calibration data loaded from the file.')
|
||||
else:
|
||||
|
|
@ -75,12 +75,11 @@ def calibrate():
|
|||
|
||||
rsp=tls.cmd(calibrate_prg)
|
||||
assert_status(rsp)
|
||||
print(rsp.hex())
|
||||
# ^ TODO check what the rest of the rsp means
|
||||
|
||||
calib_data=usb.read_82()
|
||||
print('len=%d' % len(calib_data))
|
||||
with open('calib-data.bin', 'wb') as f:
|
||||
with open(calib_data_path, 'wb') as f:
|
||||
f.write(calib_data)
|
||||
|
||||
lines=[calib_data[i:i+0x90+8] for i in range(0, len(calib_data), 0x90+8)] # TODO work out where "bytes per line" constant is comming from
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
from .tls import tls, psk_encryption_key, psk_validation_key, hs_key, crt_hardcoded
|
||||
from .tls import tls, hs_key, crt_hardcoded
|
||||
from hashlib import sha256
|
||||
import hmac
|
||||
from struct import pack, unpack
|
||||
|
|
@ -40,9 +40,9 @@ def encrypt_key(client_private, client_public):
|
|||
m = m + bytes([l])*l
|
||||
|
||||
iv = get_random_bytes(AES.block_size)
|
||||
aes = AES.new(psk_encryption_key, AES.MODE_CBC, iv)
|
||||
aes = AES.new(tls.psk_encryption_key, AES.MODE_CBC, iv)
|
||||
c = iv + aes.encrypt(m)
|
||||
sig = hmac.new(psk_validation_key, c, sha256).digest()
|
||||
sig = hmac.new(tls.psk_validation_key, c, sha256).digest()
|
||||
return b'\x02' + c + sig
|
||||
|
||||
def make_cert(client_public):
|
||||
|
|
@ -16,12 +16,6 @@ from .util import assert_status
|
|||
import pickle
|
||||
|
||||
|
||||
# Info about the host computer
|
||||
product_name, serial_number='VirtualBox', '0'
|
||||
|
||||
hw_key= bytes(product_name, 'ascii') + b'\0' + \
|
||||
bytes(serial_number, 'ascii') + b'\0'
|
||||
|
||||
password_hardcoded=unhexlify('717cd72d0962bc4a2846138dbb2c24192512a76407065f383846139d4bec2033')
|
||||
gwk_sign_hardcoded=unhexlify('3a4c76b76a97981d1274247e166610e77f4d9c9d07d3c728e532916bdd28b454')
|
||||
|
||||
|
|
@ -51,10 +45,6 @@ def prf(secret, seed, length):
|
|||
|
||||
return res[:length]
|
||||
|
||||
# pre-TLS keys
|
||||
psk_encryption_key=prf(password_hardcoded, b'GWK' + hw_key, 0x20)
|
||||
psk_validation_key=prf(psk_encryption_key, b'GWK_SIGN' + gwk_sign_hardcoded, 0x20)
|
||||
|
||||
def hs_key():
|
||||
key=password_hardcoded[:0x10]
|
||||
seed=password_hardcoded[0x10:] + b'\xaa'*2
|
||||
|
|
@ -92,12 +82,23 @@ class Tls():
|
|||
def __init__(self, usb):
|
||||
self.usb = usb
|
||||
self.reset()
|
||||
self.set_hwkey(product_name='VirtualBox', serial_number='0')
|
||||
|
||||
def reset(self):
|
||||
self.trace_enabled = False
|
||||
self.secure_rx = False
|
||||
self.secure_tx = False
|
||||
|
||||
# Info about the host computer
|
||||
def set_hwkey(self, product_name, serial_number):
|
||||
hw_key = bytes(product_name, 'ascii') + b'\0' + \
|
||||
bytes(serial_number, 'ascii') + b'\0'
|
||||
|
||||
# pre-TLS keys
|
||||
self.psk_encryption_key = prf(password_hardcoded, b'GWK' + hw_key, 0x20)
|
||||
self.psk_validation_key = prf(self.psk_encryption_key,
|
||||
b'GWK_SIGN' + gwk_sign_hardcoded, 0x20)
|
||||
|
||||
def cmd(self, cmd):
|
||||
if self.secure_rx and self.secure_tx:
|
||||
rsp=self.app(cmd)
|
||||
|
|
@ -133,6 +134,7 @@ class Tls():
|
|||
print(s)
|
||||
|
||||
def app(self, b):
|
||||
b = b() if callable(b) else b
|
||||
return self.parse_tls_response(self.usb.cmd(self.make_app_data(b)))
|
||||
|
||||
def update_neg(self, b):
|
||||
|
|
@ -491,12 +493,12 @@ class Tls():
|
|||
raise Exception('Unknown private key prefix %02x' % prefix)
|
||||
|
||||
c, hs = body[:-0x20], body[-0x20:]
|
||||
sig=hmac.new(psk_validation_key, c, sha256).digest()
|
||||
sig=hmac.new(self.psk_validation_key, c, sha256).digest()
|
||||
if hs != sig:
|
||||
raise Exception('Signature verification failed. This device was probably paired with another computer.')
|
||||
|
||||
iv, c = c[:AES.block_size], c[AES.block_size:]
|
||||
aes=AES.new(psk_encryption_key, AES.MODE_CBC, iv)
|
||||
aes=AES.new(self.psk_encryption_key, AES.MODE_CBC, iv)
|
||||
m=aes.decrypt(c)
|
||||
m=m[:-m[-1]] # unpad (standard this time)
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
from binascii import hexlify, unhexlify
|
||||
from time import ctime
|
||||
from os.path import basename
|
||||
|
||||
from .tls import tls
|
||||
from .usb import usb
|
||||
|
|
@ -8,7 +9,15 @@ from .sensor import reboot, write_hw_reg32, read_hw_reg32, identify_sensor
|
|||
from .flash import flush_changes, read_flash, erase_flash, write_flash_all, write_fw_signature, get_fw_info
|
||||
from .util import assert_status
|
||||
|
||||
def upload_fwext():
|
||||
|
||||
def default_fwext_name():
|
||||
if usb.usb_dev().idVendor == 0x138a:
|
||||
if usb.usb_dev().idProduct == 0x0090:
|
||||
return '6_07f_Lenovo.xpfwext'
|
||||
elif usb.usb_dev().idProduct == 0x0097:
|
||||
return '6_07f_lenovo_mis.xpfwext'
|
||||
|
||||
def upload_fwext(fw_path=None):
|
||||
# no idea what this is:
|
||||
write_hw_reg32(0x8000205c, 7)
|
||||
if read_hw_reg32(0x80002080) != 2:
|
||||
|
|
@ -19,7 +28,14 @@ def upload_fwext():
|
|||
# ^ TODO -- what is the real reason to detect HW at this stage?
|
||||
# just a guess: perhaps it is used to construct fwext filename
|
||||
|
||||
with open('6_07f_lenovo_mis.xpfwext', 'rb') as f:
|
||||
default_name = default_fwext_name()
|
||||
if not fw_path:
|
||||
fw_path = default_name
|
||||
elif basename(fw_path) != default_name:
|
||||
print('WARNING: Your device fw is supposed to be called {}'.format(
|
||||
default_name))
|
||||
|
||||
with open(fw_path, 'rb') as f:
|
||||
fwext=f.read()
|
||||
|
||||
fwext=fwext[fwext.index(b'\x1a')+1:]
|
||||
100
proto9x/usb.py
Normal file
100
proto9x/usb.py
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
|
||||
import usb.core as ucore
|
||||
from binascii import *
|
||||
from .util import assert_status, unhex
|
||||
from struct import unpack
|
||||
from usb.util import claim_interface, release_interface
|
||||
from queue import Queue
|
||||
from threading import Thread
|
||||
from usb.core import USBError
|
||||
from .blobs import init_hardcoded, init_hardcoded_clean_slate
|
||||
|
||||
|
||||
class Usb():
|
||||
def __init__(self):
|
||||
self.trace_enabled = False
|
||||
self.queue = Queue(maxsize=10)
|
||||
self.quit = None
|
||||
|
||||
def purge_int_queue(self):
|
||||
try:
|
||||
while True:
|
||||
self.queue.get_nowait()
|
||||
except:
|
||||
pass
|
||||
|
||||
def open(self, vendor=0x138a, product=0x0097):
|
||||
self.dev = ucore.find(idVendor=vendor, idProduct=product)
|
||||
self.dev.default_timeout = 15000
|
||||
self.thread = Thread(target=lambda: self.int_thread())
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
|
||||
def usb_dev(self):
|
||||
return self.dev
|
||||
|
||||
def send_init(self):
|
||||
#self.dev.set_configuration()
|
||||
|
||||
# TODO analyse responses, detect hardware type
|
||||
assert_status(self.cmd(unhexlify('01')))
|
||||
assert_status(self.cmd(unhexlify('19')))
|
||||
|
||||
# 43 -- get partition header(?) (02 -- fwext partition)
|
||||
# c28c745a in response is a FwextBuildtime = 0x5A748CC2
|
||||
rsp=self.cmd(unhexlify('4302'))
|
||||
|
||||
assert_status(self.cmd(init_hardcoded))
|
||||
|
||||
(err,), rsp = unpack('<H', rsp[:2]), rsp[2:]
|
||||
if err != 0:
|
||||
# fwext is not loaded
|
||||
print('Clean slate')
|
||||
self.cmd(init_hardcoded_clean_slate)
|
||||
|
||||
def cmd(self, out):
|
||||
if callable(out):
|
||||
out = out()
|
||||
if not out:
|
||||
return 0
|
||||
self.trace('>cmd> %s' % hexlify(out).decode())
|
||||
self.dev.write(1, out)
|
||||
resp = self.dev.read(129, 100*1024)
|
||||
resp = bytes(resp)
|
||||
self.trace('<cmd< %s' % hexlify(resp).decode())
|
||||
return resp
|
||||
|
||||
def read_82(self):
|
||||
try:
|
||||
resp = self.dev.read(130, 1024*1024, timeout=10000)
|
||||
resp = bytes(resp)
|
||||
self.trace('<130< %s' % hexlify(resp).decode())
|
||||
return resp
|
||||
except Exception as e:
|
||||
self.trace('<130< Error: %s' % repr(e))
|
||||
return None
|
||||
|
||||
def int_thread(self):
|
||||
try:
|
||||
while True:
|
||||
resp = self.dev.read(131, 1024, timeout=0)
|
||||
resp = bytes(resp)
|
||||
self.trace('<int< %s' % hexlify(resp).decode())
|
||||
self.queue.put(resp)
|
||||
except USBError as e:
|
||||
self.trace('<int< Exception on interrupt thread: %s' % repr(e))
|
||||
if self.quit != None:
|
||||
self.quit(e)
|
||||
finally:
|
||||
self.trace('<int< Interrupt thread is dead')
|
||||
|
||||
|
||||
def wait_int(self):
|
||||
resp = self.queue.get()
|
||||
return resp
|
||||
|
||||
def trace(self, s):
|
||||
if self.trace_enabled:
|
||||
print(s)
|
||||
|
||||
usb=Usb()
|
||||
12
prototype.py
12
prototype.py
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
from proto97.tls import tls
|
||||
from proto97.usb import usb
|
||||
from proto97.db import db
|
||||
from proto97.flash import read_flash
|
||||
from proto97.sensor import *
|
||||
from proto97.sid import *
|
||||
from proto9x.tls import tls
|
||||
from proto9x.usb import usb
|
||||
from proto9x.db import db
|
||||
from proto9x.flash import read_flash
|
||||
from proto9x.sensor import *
|
||||
from proto9x.sid import *
|
||||
|
||||
def open97():
|
||||
usb.open()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
fastecdsa==1.7.4
|
||||
pycrypto==2.6.1
|
||||
git+https://github.com/fabiant7t/pycrypto#egg=pycrypto
|
||||
pyusb==1.0.2
|
||||
|
||||
|
|
|
|||
256
validity-sensors-initializer.py
Normal file
256
validity-sensors-initializer.py
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# 2020 - Marco Trevisan
|
||||
#
|
||||
# Initializer for ThinkPad's validity sensors 0090 and 0097
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.request
|
||||
|
||||
from enum import Enum, auto
|
||||
from time import sleep
|
||||
from usb import core as usb_core
|
||||
|
||||
from proto9x.calibrate import calibrate
|
||||
from proto9x.flash import read_flash
|
||||
from proto9x.init_db import init_db
|
||||
from proto9x.init_flash import init_flash
|
||||
from proto9x.sensor import factory_reset
|
||||
from proto9x.tls import tls as vfs_tls
|
||||
from proto9x.upload_fwext import upload_fwext
|
||||
from proto9x.usb import usb as vfs_usb
|
||||
|
||||
class VFS(Enum):
|
||||
DEV_90 = 0x0090
|
||||
DEV_97 = 0x0097
|
||||
|
||||
DEFAULT_URIS = {
|
||||
VFS.DEV_90: {
|
||||
'driver': 'https://download.lenovo.com/pccbbs/mobiles/n1cgn08w.exe',
|
||||
'referral': 'https://support.lenovo.com/us/en/downloads/DS120491',
|
||||
},
|
||||
VFS.DEV_97: {
|
||||
'driver': 'https://download.lenovo.com/pccbbs/mobiles/n1mgf03w.exe',
|
||||
'referral': 'https://download.lenovo.com/pccbbs/mobiles/n1mgf03w.exe'
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_FW_NAMES = {
|
||||
VFS.DEV_90: '6_07f_Lenovo.xpfwext',
|
||||
VFS.DEV_97: '6_07f_lenovo_mis.xpfwext',
|
||||
}
|
||||
|
||||
|
||||
class VFSInitializer():
|
||||
def __init__(self, args, usb_dev, dev_type):
|
||||
self.args = args
|
||||
self.usb_dev = usb_dev
|
||||
self.dev_type = dev_type
|
||||
self.dev_str = repr(usb_dev)
|
||||
|
||||
print('Found device {}'.format(self.dev_str))
|
||||
|
||||
try:
|
||||
if self.args.simulate_virtualbox:
|
||||
raise(Exception())
|
||||
|
||||
with open('/sys/class/dmi/id/product_name', 'r') as node:
|
||||
self.product_name = node.read().strip()
|
||||
with open('/sys/class/dmi/id/product_serial', 'r') as node:
|
||||
self.product_serial = node.read().strip()
|
||||
except:
|
||||
self.product_name = 'VirtualBox'
|
||||
self.product_serial = '0'
|
||||
|
||||
if self.args.host_product:
|
||||
self.product_name = self.args.host_product
|
||||
|
||||
if self.args.host_serial:
|
||||
self.product_serial = self.args.host_serial
|
||||
|
||||
vfs_tls.set_hwkey(product_name=self.product_name,
|
||||
serial_number=self.product_serial)
|
||||
|
||||
def open_device(self, init=False):
|
||||
print('Opening device',hex(self.dev_type.value))
|
||||
vfs_usb.open(product=self.dev_type.value)
|
||||
|
||||
if init:
|
||||
vfs_usb.send_init()
|
||||
|
||||
# try to init TLS session from the flash
|
||||
vfs_tls.parseTlsFlash(read_flash(1, 0, 0x1000))
|
||||
vfs_tls.open()
|
||||
|
||||
def restart(self):
|
||||
vfs_tls.reset()
|
||||
self.open_device(init=True)
|
||||
|
||||
def download_and_extract_fw(self, fwdir, fwuri=None):
|
||||
fwuri = fwuri if fwuri else DEFAULT_URIS[self.dev_type]['driver']
|
||||
fwarchive = os.path.join(fwdir, 'fwinstaller.exe')
|
||||
fwname = DEFAULT_FW_NAMES[self.dev_type]
|
||||
|
||||
print('Downloading {} to extract {}'.format(fwuri, fwname))
|
||||
|
||||
req = urllib.request.Request(fwuri)
|
||||
req.add_header('Referer', DEFAULT_URIS[self.dev_type].get('referral', ''))
|
||||
req.add_header('User-Agent', 'Mozilla/5.0 (X11; U; Linux)')
|
||||
|
||||
with urllib.request.urlopen(req) as response:
|
||||
with open(fwarchive, 'wb') as out_file:
|
||||
out_file.write(response.read())
|
||||
|
||||
subprocess.check_call(['innoextract',
|
||||
'--output-dir', fwdir,
|
||||
'--include', fwname,
|
||||
'--collisions', 'overwrite',
|
||||
fwarchive
|
||||
])
|
||||
|
||||
fwpath = subprocess.check_output([
|
||||
'find', fwdir, '-name', fwname]).decode('utf-8').strip()
|
||||
print('Found firmware at {}'.format(fwpath))
|
||||
|
||||
if not fwpath:
|
||||
raise Exception('No {} found in the archive'.format(fwname))
|
||||
|
||||
return fwpath
|
||||
|
||||
def sleep(self, sec=3):
|
||||
print('Sleeping...')
|
||||
sleep(sec)
|
||||
|
||||
def try_factory_reset(self):
|
||||
self.open_device()
|
||||
try:
|
||||
print('Factory reset...')
|
||||
factory_reset()
|
||||
except Exception as e:
|
||||
print('Factory reset failed with {}, this should not happen, but ' \
|
||||
'we can ignore it, if pairing works...'.format(e))
|
||||
|
||||
def pair(self, fwpath):
|
||||
print('Pairing the sensor with device {}'.format(self.product_name))
|
||||
|
||||
max_retries = 5
|
||||
for i in range(0, max_retries):
|
||||
try:
|
||||
self.open_device()
|
||||
|
||||
print('Initializing flash...')
|
||||
init_flash()
|
||||
break
|
||||
except Exception as e:
|
||||
err = e
|
||||
self.sleep()
|
||||
print('Try {} failed with error: {}'.format(i+1, e))
|
||||
finally:
|
||||
max_retries -= 1
|
||||
|
||||
if max_retries == 0:
|
||||
print('Device didn\'t show up after reset, retry...')
|
||||
raise(err)
|
||||
|
||||
self.sleep()
|
||||
self.restart()
|
||||
|
||||
print('Uploading firmware...')
|
||||
upload_fwext(fw_path=fwpath)
|
||||
|
||||
self.sleep()
|
||||
self.restart()
|
||||
|
||||
if self.args.calibration_data:
|
||||
calib_data_file = self.args.calibration_data.name
|
||||
else:
|
||||
calib_data_file = 'calib-data.bin'
|
||||
|
||||
print('Calibrating, re-using {}, if any...'.format(calib_data_file))
|
||||
if os.path.exists(calib_data_file):
|
||||
calibrate(calib_data_path=calib_data_file)
|
||||
else:
|
||||
try:
|
||||
calib_data_file = os.path.join(tempfile.mkdtemp(), 'calib-data.bin')
|
||||
calibrate(calib_data_path=calib_data_file)
|
||||
print('Calibration data saved at {}'.format(calib_data_file))
|
||||
except Exception as e:
|
||||
print('Calibration failed using device data ({}), ' \
|
||||
'You can try loading a local `calib-data.bin` blob, ' \
|
||||
'the device should work anyway, so skipping...'.format(e))
|
||||
|
||||
print('Init database...')
|
||||
init_db()
|
||||
|
||||
vfs_tls.reset()
|
||||
|
||||
print('That\'s it, pairing with {} finished'.format(self.dev_str))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--driver-uri')
|
||||
parser.add_argument('--firmware-path', type=argparse.FileType('r'))
|
||||
parser.add_argument('--calibration-data', type=argparse.FileType('r'))
|
||||
parser.add_argument('--host-product')
|
||||
parser.add_argument('--host-serial')
|
||||
parser.add_argument('--simulate-virtualbox', action='store_true')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.simulate_virtualbox and (args.host_product or args.host_serial):
|
||||
parser.error("--simulate-virtualbox is incompatible with host params.")
|
||||
|
||||
if os.geteuid() != 0:
|
||||
raise Exception('This script needs to be executed as root')
|
||||
|
||||
usb_dev = None
|
||||
for d in VFS:
|
||||
dev = usb_core.find(idVendor=0x138a, idProduct=d.value)
|
||||
if dev:
|
||||
dev_type = d
|
||||
usb_dev = dev
|
||||
|
||||
if not usb_dev:
|
||||
raise Exception('No supported validity device found')
|
||||
|
||||
try:
|
||||
subprocess.check_call(['innoextract', '--version'],
|
||||
stdout=subprocess.DEVNULL)
|
||||
except Exception as e:
|
||||
print('Impossible to run innoextract: {}'.format(e))
|
||||
sys.exit(1)
|
||||
|
||||
vfs_initializer = VFSInitializer(args, usb_dev, dev_type)
|
||||
|
||||
with tempfile.TemporaryDirectory() as fwdir:
|
||||
if args.firmware_path:
|
||||
fwpath = args.firmware_path.name
|
||||
else:
|
||||
fwpath = vfs_initializer.download_and_extract_fw(fwdir,
|
||||
fwuri=args.driver_uri)
|
||||
|
||||
input('The device will be now reset to factory and associated to the ' \
|
||||
'current laptop.\nPress Enter to continue (or Ctrl+C to cancel)...')
|
||||
|
||||
vfs_initializer.try_factory_reset()
|
||||
vfs_initializer.sleep()
|
||||
vfs_initializer.pair(fwpath)
|
||||
Loading…
Reference in a new issue