├── LICENSE ├── README.md ├── ffu2img.py ├── ffu2img.py3 └── unlockerv.py /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 t0x0 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Random projects 2 | 3 | ----- 4 | 2017-01-17: I will be rewriting this for the current iteration of Windows IOT images for all supported devices, and will be only working on the Python 3 version. 5 | ----- 6 | ffu2img.py - python script to convert Microsoft FFU files to raw disk images (tested with python 2.7.9) 7 | ffu2img.py3 - python script to convert Microsoft FFU files to raw disk images (tested with python 3.4.3) 8 | -------------------------------------------------------------------------------- /ffu2img.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # MIT License, Copyright 2015 t0x0 4 | # Full text in 'LICENSE' file 5 | 6 | # May not work for any FFU files other than the Raspberry Pi 2 Windows 10 Insider Preview image. 7 | # Tested on the 2015-05-12 release image with Python 2.7.9 8 | # Use at your own risk, and let me know if it fails for your situation. me@t0x0.com 9 | 10 | import sys, os.path, struct, string 11 | from collections import namedtuple 12 | 13 | if len(sys.argv) < 2: 14 | sys.exit("Error: no filenames provided. Usage: ffu2img.py input.ffu [output.img]\nWarning, will overwrite output file without prior permission.") 15 | ffupath = sys.argv[1] 16 | if len(sys.argv) == 3: 17 | imgpath = sys.argv[2] 18 | else: 19 | imgpath = string.rsplit(ffupath, '.', 1)[0] + '.img' 20 | print 'Input File: ' + ffupath 21 | print 'Output File: ' + imgpath 22 | 23 | SecurityHeader = namedtuple("SecurityHeader", "cbSize signature dwChunkSizeInKb dwAlgId dwCatalogSize dwHashTableSize") 24 | ImageHeader = namedtuple("ImageHeader", "cbSize signature ManifestLength dwChunkSize") 25 | StoreHeader = namedtuple("StoreHeader", "dwUpdateType MajorVersion MinorVersion FullFlashMajorVersion FullFlashMinorVersion szPlatformId dwBlockSizeInBytes dwWriteDescriptorCount dwWriteDescriptorLength dwValidateDescriptorCount dwValidateDescriptorLength dwInitialTableIndex dwInitialTableCount dwFlashOnlyTableIndex dwFlashOnlyTableCount dwFinalTableIndex dwFinalTableCount") 26 | BlockDataEntry = namedtuple("BlockDataEntry", "dwDiskAccessMethod dwBlockIndex dwLocationCount dwBlockCount") 27 | 28 | ffufp = open(ffupath, 'rb') 29 | imgfp = open(imgpath, 'wb') 30 | logfp = open('ffu2img.log', 'w') 31 | 32 | def readsecheader(): 33 | (cbSize, signature, dwChunkSizeInKb, dwAlgId, dwCatalogSize, dwHashTableSize) = struct.unpack(" 1: 98 | print('\r' + str(iBlock) + ' blocks, ' + str((iBlock*FFUStoreHeader.dwBlockSizeInBytes)/1024) + 'kb written - Delay expected. Please wait.'), 99 | oldblockcount = CurrentBlockDataEntry.dwBlockCount 100 | for key, val in CurrentBlockDataEntry._asdict().iteritems(): 101 | logfp.write(key + ' = ' + str(val) + '\n') 102 | curraddress = ffufp.tell() 103 | ffufp.seek(blockdataaddress+(iBlock*FFUStoreHeader.dwBlockSizeInBytes)) 104 | imgfp.seek(CurrentBlockDataEntry.dwBlockCount*FFUStoreHeader.dwBlockSizeInBytes) 105 | imgfp.write(ffufp.read(FFUStoreHeader.dwBlockSizeInBytes)) 106 | ffufp.seek(curraddress) 107 | iBlock = iBlock + 1 108 | print '\nWrite complete.' 109 | imgfp.close() 110 | ffufp.close() 111 | logfp.close() 112 | -------------------------------------------------------------------------------- /ffu2img.py3: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # MIT License, Copyright 2015 t0x0 4 | # Full text in 'LICENSE' file 5 | 6 | # May not work for any FFU files other than the Raspberry Pi 2 Windows 10 Insider Preview image. 7 | # Tested on the 2015-05-12 release image with Python 3.4.3 8 | # Use at your own risk, and let me know if it fails for your situation. me@t0x0.com 9 | 10 | import sys, os.path, struct, string 11 | from collections import namedtuple 12 | 13 | if len(sys.argv) < 2: 14 | sys.exit("Error: no filenames provided. Usage: ffu2img.py3 input.ffu [output.img]\nWarning, will overwrite output file without prior permission.") 15 | ffupath = sys.argv[1] 16 | if len(sys.argv) == 3: 17 | imgpath = sys.argv[2] 18 | else: 19 | imgpath = sys.argv[1].rsplit('.', 1)[0] + '.img' 20 | print('Input File: ' + ffupath) 21 | print('Output File: ' + imgpath) 22 | 23 | SecurityHeader = namedtuple("SecurityHeader", "cbSize signature dwChunkSizeInKb dwAlgId dwCatalogSize dwHashTableSize") 24 | ImageHeader = namedtuple("ImageHeader", "cbSize signature ManifestLength dwChunkSize") 25 | StoreHeader = namedtuple("StoreHeader", "dwUpdateType MajorVersion MinorVersion FullFlashMajorVersion FullFlashMinorVersion szPlatformId dwBlockSizeInBytes dwWriteDescriptorCount dwWriteDescriptorLength dwValidateDescriptorCount dwValidateDescriptorLength dwInitialTableIndex dwInitialTableCount dwFlashOnlyTableIndex dwFlashOnlyTableCount dwFinalTableIndex dwFinalTableCount") 26 | BlockDataEntry = namedtuple("BlockDataEntry", "dwDiskAccessMethod dwBlockIndex dwLocationCount dwBlockCount") 27 | 28 | ffufp = open(ffupath, 'rb') 29 | imgfp = open(imgpath, 'wb') 30 | logfp = open('ffu2img.log', 'w') 31 | 32 | def readsecheader(): 33 | (cbSize, signature, dwChunkSizeInKb, dwAlgId, dwCatalogSize, dwHashTableSize) = struct.unpack(" 1: 98 | print((str(iBlock) + ' blocks, ' + str((iBlock*FFUStoreHeader.dwBlockSizeInBytes)/1024) + 'kb written - Delay expected. Please wait.'), end='\r') 99 | oldblockcount = CurrentBlockDataEntry.dwBlockCount 100 | for key, val in CurrentBlockDataEntry._asdict().items(): 101 | logfp.write(key + ' = ' + str(val) + '\n') 102 | curraddress = ffufp.tell() 103 | ffufp.seek(blockdataaddress+(iBlock*FFUStoreHeader.dwBlockSizeInBytes)) 104 | imgfp.seek(CurrentBlockDataEntry.dwBlockCount*FFUStoreHeader.dwBlockSizeInBytes) 105 | imgfp.write(ffufp.read(FFUStoreHeader.dwBlockSizeInBytes)) 106 | ffufp.seek(curraddress) 107 | iBlock = iBlock + 1 108 | print('\nWrite complete.') 109 | imgfp.close() 110 | ffufp.close() 111 | logfp.close() 112 | 113 | -------------------------------------------------------------------------------- /unlockerv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # MIT License, Copyright 2015 t0x0 4 | # Full text in 'LICENSE' file 5 | 6 | # Will not work for any ransomware other than "Locker v*" by Poka Brightminds. 7 | # Untested. Alpha code. Use at your own risk. 8 | # Do not drink, recycled code in use. Code green. Feedback: me@t0x0.com 9 | 10 | # Prerequisite: pycrypto - https://www.dlitz.net/software/pycrypto/ 11 | # Python 2.7.9 and pycrypto 2.6.1 12 | 13 | import sys, os, struct, string, httplib, copy 14 | import pyaes 15 | from Crypto.Cipher import PKCS1_OAEP 16 | from Crypto.Hash import SHA 17 | from Crypto.PublicKey import RSA 18 | from xml.dom import minidom 19 | from base64 import b64decode 20 | 21 | if len(sys.argv) < 3: 22 | sys.exit("Error: incorrect arguments. Usage: unlockerv.py [decrypted file name]\nWarning, will overwrite output file without prior permission.") 23 | encpath = sys.argv[1] 24 | btcaddress = sys.argv[2] 25 | if len(sys.argv) == 4: 26 | decpath = sys.argv[3] 27 | else: 28 | splitencpath = string.rsplit(encpath, '.', 1) 29 | decpath = splitencpath[0] + '.decrypted.' + splitencpath[1] 30 | print 'Input File: ' + encpath 31 | print 'Output File: ' + decpath 32 | print 'Bitcoin Address: ' + btcaddress 33 | 34 | encfp = open(encpath, 'rb') 35 | decfp = open(decpath, 'wb') 36 | 37 | #Get btc address/keys via HTTP 38 | conn = httplib.HTTPConnection("www.t0x0.com") 39 | conn.request("GET", "/lockervkey/" + btcaddress) 40 | res = conn.getresponse() 41 | if res.status != 200: 42 | sys.exit("Error: bitcoin address not found. Please check the address and try again.\n If it is still not found, the keys are not available and decryption can not proceed.") 43 | keystring = "" + string.translate(res.read() + "", None, ",") 44 | 45 | xmldoc = minidom.parseString(keystring) 46 | (key1, key2) = xmldoc.getElementsByTagName('RSAKeyValue') 47 | 48 | modulusraw = b64decode(key1.childNodes[0].childNodes[0].nodeValue) 49 | modulus = long(eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in modulusraw]))) 50 | exponentraw = b64decode(key1.childNodes[1].childNodes[0].nodeValue) 51 | exponent = long(eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in exponentraw]))) 52 | praw = b64decode(key2.childNodes[2].childNodes[0].nodeValue) 53 | p = long(eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in praw]))) 54 | draw = b64decode(key2.childNodes[7].childNodes[0].nodeValue) 55 | d = long(eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in draw]))) 56 | qraw = b64decode(key2.childNodes[3].childNodes[0].nodeValue) 57 | q = long(eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in qraw]))) 58 | qinvraw = b64decode(key2.childNodes[6].childNodes[0].nodeValue) 59 | qinv = long(eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in qinvraw]))) 60 | r = RSA.construct((modulus, exponent, d, p, q)) 61 | h = SHA.new() 62 | cipher = PKCS1_OAEP.new(r, h) 63 | 64 | (headerlen) = struct.unpack("