| 1 |  |  | #!/usr/bin/env python3 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  | """This module is the Python-ized implementation of cap.exe""" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  | import os  # path work | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | import binascii  # to hex and back again | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | import glob  # filename matching | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | import base64  # storage | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | from bbarchivist import bbconstants  # versions/constants | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | from bbarchivist import utilities  # finding cap | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | __author__ = "Thurask" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | __license__ = "WTFPL v2" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | __copyright__ = "Copyright 2015-2016 Thurask" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | def ghetto_convert(intsize): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  |     Convert from decimal integer to little endian | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  |     hexadecimal string, padded to 16 characters with zeros. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  |     :param intsize: Integer you wish to convert. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |     :type intsize: int | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  |     if not isinstance(intsize, int): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  |         intsize = int(intsize) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  |     hexsize = format(intsize, '08x')  # '00AABBCC' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  |     newlist = [hexsize[i:i + 2] for i in range(0, len(hexsize), 2)]  # ['00', 'AA','BB','CC'] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  |     newlist.reverse() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  |     ghetto_hex = "".join(newlist)  # 'CCBBAA' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  |     ghetto_hex = ghetto_hex.rjust(16, '0') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  |     if len(ghetto_hex) == 16: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  |         return binascii.unhexlify(bytes(ghetto_hex.upper(), 'ascii')) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  | def make_sizes(filelist): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |     Get sizes of list of signed files. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |     :param filelist: List of 1-6 signed files. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  |     :type filelist: list(str) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  |     first = str(glob.glob(filelist[0])[0]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |     size1 = os.path.getsize(first)  # required | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  |     size2 = size3 = size4 = size5 = 0 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  |     if len(filelist) >= 2: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |         second = str(glob.glob(filelist[1])[0]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  |         size2 = os.path.getsize(second) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  |     if len(filelist) >= 3: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  |         third = str(glob.glob(filelist[2])[0]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  |         size3 = os.path.getsize(third) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  |     if len(filelist) >= 4: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |         fourth = str(glob.glob(filelist[3])[0]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |         size4 = os.path.getsize(fourth) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  |     if len(filelist) >= 5: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |         fifth = str(glob.glob(filelist[4])[0]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  |         size5 = os.path.getsize(fifth) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |     return [size1, size2, size3, size4, size5] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  | def make_starts(beginlength, capsize, pad, sizes, filecount): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  |     Get list of starting positions for each signed file. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |     :param beginlength: Length of beginning offset. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |     :type beginlength: int | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |     :param capsize: Size of cap executable. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |     :type capsize: int | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  |     :param pad: Padding character. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  |     :type pad: bytes | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  |     :param sizes: List of signed file sizes. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  |     :type sizes: list(int) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  |     | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 |  |  |     :param filecount: Number of signed files. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 |  |  |     :type filecount: int | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 |  |  |     firstoffset = beginlength + capsize | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |     start1 = ghetto_convert(firstoffset) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |     start2 = start3 = start4 = start5 = start6 = pad * 8 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  |     if filecount >= 2: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  |         secondoffset = firstoffset + sizes[0]  # start of second file | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |         start2 = ghetto_convert(secondoffset) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  |     if filecount >= 3: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  |         thirdoffset = secondoffset + sizes[1]  # start of third file | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |         start3 = ghetto_convert(thirdoffset) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  |     if filecount >= 4: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |         fourthoffset = thirdoffset + sizes[2]  # start of fourth file | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |         start4 = ghetto_convert(fourthoffset) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  |     if filecount >= 5: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |         fifthoffset = fourthoffset + sizes[3]  # start of fifth file | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |         start5 = ghetto_convert(fifthoffset) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |     if filecount == 6: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |         sixthoffset = fifthoffset + sizes[4]  # start of sixth file | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |         start6 = ghetto_convert(sixthoffset) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  |     return [start1, start2, start3, start4, start5, start6] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |  | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 99 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 100 |  |  | def make_offset(files, folder=None): | 
            
                                                                        
                            
            
                                    
            
            
                | 101 |  |  |     """ | 
            
                                                                        
                            
            
                                    
            
            
                | 102 |  |  |     Create magic offset file for use in autoloader creation. | 
            
                                                                        
                            
            
                                    
            
            
                | 103 |  |  |     Cap.exe MUST match separator version. | 
            
                                                                        
                            
            
                                    
            
            
                | 104 |  |  |     Version defined in :data:`bbarchivist.bbconstants.CAP.version`. | 
            
                                                                        
                            
            
                                    
            
            
                | 105 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 106 |  |  |     :param files: List of 1-6 signed files. | 
            
                                                                        
                            
            
                                    
            
            
                | 107 |  |  |     :type files: list(str) | 
            
                                                                        
                            
            
                                    
            
            
                | 108 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 109 |  |  |     :param folder: Working folder. Optional. Default is local. | 
            
                                                                        
                            
            
                                    
            
            
                | 110 |  |  |     :type folder: str | 
            
                                                                        
                            
            
                                    
            
            
                | 111 |  |  |     """ | 
            
                                                                        
                            
            
                                    
            
            
                | 112 |  |  |     folder = os.getcwd() if folder is None else folder | 
            
                                                                        
                            
            
                                    
            
            
                | 113 |  |  |     capfile = utilities.grab_cap() | 
            
                                                                        
                            
            
                                    
            
            
                | 114 |  |  |     filelist = [file for file in files if file] | 
            
                                                                        
                            
            
                                    
            
            
                | 115 |  |  |     filecount = len(filelist) | 
            
                                                                        
                            
            
                                    
            
            
                | 116 |  |  |     fcount = b'0' + bytes(str(filecount), 'ascii') | 
            
                                                                        
                            
            
                                    
            
            
                | 117 |  |  |     # immutable things | 
            
                                                                        
                            
            
                                    
            
            
                | 118 |  |  |     scaff = b'at9dFE5LTEdOT0hHR0lTCxcKDR4MFFMtPiU6LT0zPjs6Ui88U05GTVFOSUdRTlFOT3BwcJzVxZec1cWXnNXFlw==' | 
            
                                                                        
                            
            
                                    
            
            
                | 119 |  |  |     separator = base64.b64decode(scaff) | 
            
                                                                        
                            
            
                                    
            
            
                | 120 |  |  |     password = binascii.unhexlify(b'0' * 160) | 
            
                                                                        
                            
            
                                    
            
            
                | 121 |  |  |     pad = b'\x00'  # 1x, 2x or 8x | 
            
                                                                        
                            
            
                                    
            
            
                | 122 |  |  |     filepad = binascii.unhexlify(fcount)  # 01-06 | 
            
                                                                        
                            
            
                                    
            
            
                | 123 |  |  |     trailers = binascii.unhexlify(b'00' * (7 - filecount))  # 00, 1-6x | 
            
                                                                        
                            
            
                                    
            
            
                | 124 |  |  |     capsize = os.path.getsize(capfile) | 
            
                                                                        
                            
            
                                    
            
            
                | 125 |  |  |     if not filecount:  # we need at least one file | 
            
                                                                        
                            
            
                                    
            
            
                | 126 |  |  |         raise SystemExit | 
            
                                                                        
                            
            
                                    
            
            
                | 127 |  |  |     sizes = make_sizes(filelist) | 
            
                                                                        
                            
            
                                    
            
            
                | 128 |  |  |     # start of first file; length of cap + length of offset | 
            
                                                                        
                            
            
                                    
            
            
                | 129 |  |  |     beginlength = len(separator) + len(password) + 64 | 
            
                                                                        
                            
            
                                    
            
            
                | 130 |  |  |     starts = make_starts(beginlength, capsize, pad, sizes, filecount) | 
            
                                                                        
                            
            
                                    
            
            
                | 131 |  |  |     makeuplen = 64 - 6 * len(pad * 8) - 2 * len(pad * 2) - 2 * \ | 
            
                                                                        
                            
            
                                    
            
            
                | 132 |  |  |         len(pad) - len(trailers) - len(filepad) | 
            
                                                                        
                            
            
                                    
            
            
                | 133 |  |  |     makeup = b'\x00' * makeuplen  # pad to match offset begin | 
            
                                                                        
                            
            
                                    
            
            
                | 134 |  |  |     with open(os.path.join(folder, "offset.hex"), "wb") as file: | 
            
                                                                        
                            
            
                                    
            
            
                | 135 |  |  |         file.write(separator) | 
            
                                                                        
                            
            
                                    
            
            
                | 136 |  |  |         file.write(password) | 
            
                                                                        
                            
            
                                    
            
            
                | 137 |  |  |         file.write(filepad) | 
            
                                                                        
                            
            
                                    
            
            
                | 138 |  |  |         file.write(pad * 2) | 
            
                                                                        
                            
            
                                    
            
            
                | 139 |  |  |         file.write(pad) | 
            
                                                                        
                            
            
                                    
            
            
                | 140 |  |  |         file.write(starts[0]) | 
            
                                                                        
                            
            
                                    
            
            
                | 141 |  |  |         file.write(starts[1]) | 
            
                                                                        
                            
            
                                    
            
            
                | 142 |  |  |         file.write(starts[2]) | 
            
                                                                        
                            
            
                                    
            
            
                | 143 |  |  |         file.write(starts[3]) | 
            
                                                                        
                            
            
                                    
            
            
                | 144 |  |  |         file.write(starts[4]) | 
            
                                                                        
                            
            
                                    
            
            
                | 145 |  |  |         file.write(starts[5]) | 
            
                                                                        
                            
            
                                    
            
            
                | 146 |  |  |         file.write(pad) | 
            
                                                                        
                            
            
                                    
            
            
                | 147 |  |  |         file.write(pad * 2) | 
            
                                                                        
                            
            
                                    
            
            
                | 148 |  |  |         file.write(trailers) | 
            
                                                                        
                            
            
                                    
            
            
                | 149 |  |  |         file.write(makeup) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  | def write_4k(infile, outfile, text="FILE"): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 |  |  |     Write a file from another file, 4k bytes at a time. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 |  |  |     :param infile: Filename. Input file. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 |  |  |     :type infile: str | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 |  |  |     :param outfile: Open (!!!) file handle. Output file. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 |  |  |     :type outfile: str | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 162 |  |  |     :param text: Writing <text>... | 
            
                                                                                                            
                            
            
                                    
            
            
                | 163 |  |  |     :type text: str | 
            
                                                                                                            
                            
            
                                    
            
            
                | 164 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 165 |  |  |     with open(os.path.abspath(infile), "rb") as afile: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 166 |  |  |         print("WRITING {1}...\n{0}".format(os.path.basename(infile), text)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 167 |  |  |         while True: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 168 |  |  |             chunk = afile.read(4096)  # 4k chunks | 
            
                                                                                                            
                            
            
                                    
            
            
                | 169 |  |  |             if not chunk: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 170 |  |  |                 break | 
            
                                                                                                            
                            
            
                                    
            
            
                | 171 |  |  |             outfile.write(chunk) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 172 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 173 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 174 |  |  | def make_autoloader(filename, files, folder=None): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 175 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 176 |  |  |     Write cap.exe, magic offset, signed files to a .exe file. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 177 |  |  |     :func:`make_offset` is used to create the offset. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 178 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 179 |  |  |     :param filename: Name of autoloader. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 180 |  |  |     :type filename: str | 
            
                                                                                                            
                            
            
                                    
            
            
                | 181 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 182 |  |  |     :param files: List of 1-6 signed files. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 183 |  |  |     :type files: list(str) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 184 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 185 |  |  |     :param folder: Working folder. Optional. Default is local. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 186 |  |  |     :type folder: str | 
            
                                                                                                            
                            
            
                                    
            
            
                | 187 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 188 |  |  |     folder = os.getcwd() if folder is None else folder | 
            
                                                                                                            
                            
            
                                    
            
            
                | 189 |  |  |     make_offset(files, folder) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 190 |  |  |     filelist = [os.path.abspath(file) for file in files if file] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 191 |  |  |     print("CREATING: {0}".format(filename)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 192 |  |  |     try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 193 |  |  |         with open(os.path.join(os.path.abspath(folder), filename), "wb") as autoloader: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 194 |  |  |             capskel = "CAP VERSION {0}".format(bbconstants.CAP.version) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 195 |  |  |             write_4k(os.path.normpath(utilities.grab_cap()), autoloader, capskel) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 196 |  |  |             write_4k(os.path.join(folder, "offset.hex"), autoloader, "MAGIC OFFSET") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 197 |  |  |             for file in filelist: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 198 |  |  |                 write_4k(file, autoloader) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 199 |  |  |     except IOError as exc: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 200 |  |  |         print("Operation failed: {0}".format(exc.strerror)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 201 |  |  |     else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 202 |  |  |         print("{0} FINISHED!".format(filename)) | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 203 |  |  |     os.remove(os.path.join(folder, "offset.hex")) | 
            
                                                        
            
                                    
            
            
                | 204 |  |  |  |