forked from ccbrown/teslacam-recovery
-
Notifications
You must be signed in to change notification settings - Fork 0
/
run.py
executable file
·227 lines (185 loc) · 8.39 KB
/
run.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/usr/bin/python
import os
import sys
import struct
# Used for debugging.
def dumpBinary(data):
offset = 0
for byte in data:
print '%02X ' % ord(byte),
offset += 1
if offset % 16 == 0:
print
elif offset % 8 == 0:
print ' ',
return
if len(sys.argv) < 3:
print 'Usage: '+sys.argv[0]+' input outputdir'
sys.exit()
f = open(sys.argv[1], 'rb')
bootSector = f.read(512)
bytesPerSectorShift, = struct.unpack("<B", bootSector[0x6C]) # 1 byte, The number of places one would need to shift to translate bytes to sectors or sectors to bytes
sectorsPerClusterShift, = struct.unpack("<B", bootSector[0x6D]) # 1 byte, Similar to the above, except used to calculate sectors per cluster.
reservedSectors, = struct.unpack("<I", bootSector[0x50:0x54]) # FATOffset, 4 bytes, offset in sectors from the start of the partition to the first FAT
numberOfFATs, = struct.unpack("<B", bootSector[0x6E]) # 1 byte
#maxRootDirEntries, = struct.unpack("<H", bootSector[0x11:0x13])
totalSectors, = struct.unpack("<Q", bootSector[0x48:0x50]) # VolumeLength in Sectors, 8 bytes
sectorsPerFAT, = struct.unpack("<I", bootSector[0x54:0x58]) # FATLength, 4 bytes
#rootDirectoryCluster, = struct.unpack("<I", bootSector[0x2c:0x30])
bytesPerSector = 2 ** bytesPerSectorShift
sectorsPerCluster = 2 ** sectorsPerClusterShift
maxRootDirEntries = 0
# if totalSectors == 0:
# totalSectors, = struct.unpack("<I", bootSector[0x20:0x24])
totalClusters = totalSectors/sectorsPerCluster
bytesPerCluster = bytesPerSector*sectorsPerCluster
print 'Bytes per sector: %d' % bytesPerSector
print 'Sectors per cluster: %d' % sectorsPerCluster
print 'Reserved sectors: %d' % reservedSectors
print 'Number of FATs: %d' % numberOfFATs
print 'Maximum root directory entries: %d' % maxRootDirEntries
print 'Total sectors: %d' % totalSectors
print 'Total clusters: %d' % totalClusters
print 'Sectors per FAT: %d' % sectorsPerFAT
#print 'Root Directory Cluster: %d' % rootDirectoryCluster
def sect2byte(sect):
return sect << bytesPerSectorShift
def clust2byte(clust):
return sect2byte(reservedSectors+numberOfFATs*sectorsPerFAT+maxRootDirEntries*32/bytesPerSector+(clust-2)*sectorsPerCluster)
def isNonzero(data):
for x in data:
if ord(x) != 0x00:
return True
return False
fatEntries = list()
print 'Reading FATs at offset %d . . .' % sect2byte(reservedSectors)
f.seek(sect2byte(reservedSectors))
for i in range(0, numberOfFATs):
fat = f.read(sectorsPerFAT*bytesPerSector)
j = 0
while j < sectorsPerFAT*bytesPerSector:
entry, = struct.unpack("<I", fat[j:j+4])
fatEntries.append(entry & 0x0FFFFFFF)
j += 4
def readDirectory(directoryCluster, tabs, path='/', root=False):
pad = list()
for i in range(0, tabs):
pad.append('\t')
pad = ''.join(pad)
while True:
f.seek(clust2byte(directoryCluster))
lfn = ''
for i in range(bytesPerCluster / 32):
entry = f.read(32)
shortFilename = entry[0:8]
extension = entry[8:11]
attributes = ord(entry[11])
lastModifiedTime, = struct.unpack("<H", entry[0x16:0x18])
lastModifiedDate, = struct.unpack("<H", entry[0x18:0x1a])
clusterLow, = struct.unpack("<H", entry[0x1a:0x1c])
clusterHigh, = struct.unpack("<H", entry[0x14:0x16])
filesize, = struct.unpack("<I", entry[28:32])
cluster = (clusterHigh << 16) | clusterLow
filename = lfn.replace('\xff', '').replace(
'\0', '') if lfn != '' else ('%s.%s' % (shortFilename, extension))
if attributes == 0 and cluster == 0:
continue
if attributes != 0x0F:
# Not a long file name.
print '\n%sFilename: %s' % (pad, filename)
print '%sAttributes: 0x%02X' % (pad, attributes)
print '%sFilesize: %d' % (pad, filesize)
print '%sLast Modified: %d-%d-%d %d:%02d:%02d' % (pad,
1980 +
((lastModifiedDate & 0xfe00) >> 9), (
lastModifiedDate & 0x1e0) >> 5, lastModifiedDate & 0x1f,
(lastModifiedTime & 0xf800) >> 11, (
lastModifiedTime & 0x7e0) >> 5, lastModifiedTime & 0x1f
)
lfn = ''
firstCluster = cluster
curCluster = cluster
lastCluster = firstCluster - 1
while curCluster < 0x0FFFFFF0:
if curCluster != lastCluster + 1:
print '%sClusters: %d - %d' % (pad,
firstCluster, lastCluster)
firstCluster = curCluster
lastCluster = curCluster
curCluster = fatEntries[curCluster]
print '%sClusters: %d - %d' % (pad, firstCluster, lastCluster)
if attributes == 0x0F:
# Long file name.
lfn = entry[0x01:0x01+10] + \
entry[0x0e:0x0e+12] + entry[0x1c:0x1c+4] + lfn
elif attributes & 0x08:
# Volume label.
pass
elif attributes & 0x10 and shortFilename != '. ' and shortFilename != '.. ':
print '%sEntering directory . . .' % pad
# Create the directory structure.
try:
os.mkdir('%s%s%s' % (sys.argv[2], path, filename))
except OSError:
pass
previousPosition = f.tell()
readDirectory(cluster, tabs+1, '%s%s/' % (path, filename))
f.seek(previousPosition)
elif shortFilename != '. ' and shortFilename != '.. ' and filesize != 0xFFFFFFFF:
# Modify this condition if you want to export some files.
if True:
continue
# Export the files.
previousPosition = f.tell()
fout = open('%s%s%s' % (sys.argv[2], path, filename), 'wb')
# For recovering files, it may be useful to assume contiguous clusters.
contiguous = True
if contiguous:
f.seek(clust2byte(cluster))
fout.write(f.read(filesize))
else:
curCluster = cluster
while curCluster < 0x0FFFFFF0:
f.seek(clust2byte(curCluster))
fout.write(f.read(bytesPerCluster))
fout.close()
f.seek(previousPosition)
directoryCluster = fatEntries[directoryCluster]
if (directoryCluster & 0xFFFFFF8) == 0xFFFFFF8:
break
def extractMP4s(startCluster, endCluster, maxSize):
try:
os.mkdir('%s/mp4s' % (sys.argv[2]))
except OSError:
pass
cluster = startCluster
while cluster < endCluster:
if cluster % 1000 == 0:
print 'Currently on cluster %d' % (cluster)
f.seek(clust2byte(cluster))
header = f.read(12)
if header == '\0\0\0 ftypmp42':
print 'Found mp4 at cluster %d' % (cluster)
fout = open('%s/mp4s/%d.mp4' % (sys.argv[2], cluster), 'wb')
curCluster = cluster
bytesWritten = 0
while True:
f.seek(clust2byte(curCluster))
data = f.read(bytesPerCluster)
if bytesWritten > 0 and data[0:12] == '\0\0\0 ftypmp42':
break
fout.write(data)
bytesWritten += bytesPerCluster
curCluster += 1
if bytesWritten >= maxSize:
break
fout.close()
cluster += 1
# If you want to do a faster, more targeted search, you can uncomment these lines and use the output
# to specify a cluster range to extractMP4s below:
#print 'Reading directories...'
#readDirectory(rootDirectoryCluster, 1)
print 'Finding mp4s...'
extractMP4s(0, totalClusters, 40000000)
print
f.close()