Advertisement
Nicknine

SIM/SID Extractor

May 10th, 2022 (edited)
1,039
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.88 KB | None | 0 0
  1. import os
  2. import sys
  3. import struct
  4. import io
  5. import zipfile
  6.  
  7. import vdf
  8. from Crypto.Cipher import AES
  9.  
  10. def readNullTerminatedString(f,offset):
  11.     prevOffset=f.tell()
  12.     f.seek(offset)
  13.  
  14.     result=b""
  15.     while 1:
  16.         byte=f.read(1)
  17.         if byte==b"\x00": break
  18.         result+=byte
  19.  
  20.     f.seek(prevOffset)
  21.     return result.decode()
  22.  
  23. volHandles=dict()
  24.  
  25. class Volume:
  26.     def __init__(self,volName):
  27.         self.f=open(volName,"rb")
  28.         self.size=os.fstat(self.f.fileno()).st_size
  29.  
  30. def openVolume(baseName,disc,vol):
  31.     volName="%s_disk%d_%d.sid" % (baseName,disc,vol)
  32.  
  33.     if vol not in volHandles:
  34.         while not os.path.isfile(volName):
  35.             # Changing disc - close all volumes.
  36.             closeVolumes()
  37.             input("Need %s, insert disc %d and press Enter..." % (os.path.basename(volName),disc))
  38.  
  39.         volHandles[vol]=Volume(volName)
  40.  
  41.     return volHandles[vol].f
  42.  
  43. def openNextVolume(baseName,disc,vol):
  44.     vol+=1
  45.     volName="%s_disk%d_%d.sid" % (baseName,disc,vol)
  46.     if not os.path.isfile(volName):
  47.         # Change disc.
  48.         disc+=1
  49.         vol=0
  50.  
  51.     f=openVolume(baseName,disc,vol)
  52.     f.seek(0x00)
  53.     return f,disc,vol
  54.  
  55. def getVolumeSize(vol):
  56.     return volHandles[vol].size
  57.  
  58. def closeVolumes():
  59.     for vol in volHandles.values():
  60.         vol.f.close()
  61.     volHandles.clear()
  62.  
  63. def extractDepot(baseName,curDisc,targetDepot,keystring,outDir):
  64.     if keystring:
  65.         key=bytes.fromhex(keystring+"A8194D02193CD03792937D27590AECBD")
  66.     else:
  67.         key=None
  68.  
  69.     f=open("%s_disk%d.sim" % (baseName,curDisc),"rb")
  70.     magic,version,discs,stringsSize=struct.unpack("<IIII",f.read(0x10))
  71.     if magic!=0x3FD04C1F: raise Exception("Bad header magic in %s" % (f.name))
  72.     if version!=1: raise Exception("Unknown version %d in %s" % (version,f.name))
  73.  
  74.     s=io.BytesIO(f.read(stringsSize))
  75.     tableSize,numFiles=struct.unpack("<II",f.read(0x08))
  76.     t=io.BytesIO(f.read(tableSize))
  77.     f.close()
  78.  
  79.     for i in range(numFiles):
  80.         nameOffset,pathOffset,depot,offset,size,disc,vol,isEnc,pad=struct.unpack("<IIIQQBBBB",t.read(0x20))
  81.         if depot!=targetDepot:
  82.             continue
  83.  
  84.         name=readNullTerminatedString(s,nameOffset)
  85.         path=readNullTerminatedString(s,pathOffset)
  86.         print(os.path.join(path,name))
  87.         #print(t.tell()-0x20)
  88.  
  89.         if isEnc and not key:
  90.             print("File is encrypted and no depot key was provided! Skipping...")
  91.             continue
  92.  
  93.         f2=openVolume(baseName,disc,vol)
  94.         f2.seek(offset)
  95.         #print("%d %d 0x%08x" % (disc,vol,offset))
  96.  
  97.         os.makedirs(os.path.join(outDir,path),exist_ok=True)
  98.         fname=os.path.join(outDir,path,name)
  99.         out=open(fname,"wb")
  100.  
  101.         bytesWritten=0
  102.         while bytesWritten!=size:
  103.             if f2.tell()==getVolumeSize(vol):
  104.                 # Reached EOF, open next volume.
  105.                 f2,disc,vol=openNextVolume(baseName,disc,vol)
  106.  
  107.             chunkZSize,chunkSize=struct.unpack("<II",f2.read(0x08))
  108.             padSize=(chunkZSize>>24)&0xFF
  109.             chunkZSize&=0x00FFFFFF
  110.             flags=(chunkSize>>24)&0xFF
  111.             chunkSize&=0x00FFFFFF
  112.  
  113.             data=f2.read(chunkZSize)
  114.             bytesWritten+=chunkSize
  115.  
  116.             if flags&0x01:
  117.                 if not key:
  118.                     print("Encountered encrypted chunk and no depot key was provided! Skipping the file...")
  119.                     out.close()
  120.                     os.remove(fname)
  121.                     break
  122.  
  123.                 decSize=chunkZSize-padSize
  124.  
  125.                 # Decrypt IV.
  126.                 cipher=AES.new(key,AES.MODE_ECB)
  127.                 iv=cipher.decrypt(data[:0x10])
  128.  
  129.                 # Decrypt data.
  130.                 cipher=AES.new(key,AES.MODE_CBC,iv=iv)
  131.                 data=cipher.decrypt(data[0x10:])[:decSize]
  132.  
  133.             if flags&0x02:
  134.                 zipf=zipfile.ZipFile(io.BytesIO(data))
  135.                 out.write(zipf.read("zip"))
  136.                 zipf.close()
  137.             else:
  138.                 out.write(data)
  139.  
  140.         out.close()
  141.  
  142.     closeVolumes()
  143.  
  144. if __name__=="__main__":
  145.     if len(sys.argv)<4:
  146.         print("Usage: sim_extract.py <simpath> <depotid> <outpath>")
  147.         sys.exit(0)
  148.  
  149.     fname=sys.argv[1]
  150.     depotStr=sys.argv[2]
  151.     outDir=sys.argv[3]
  152.  
  153.     if fname[-4:]!=".sim":
  154.         raise Exception("Not a SIM file")
  155.  
  156.     kv=vdf.load(open("legacydepotdata.vdf","r"))
  157.     if depotStr in kv["depots"]:
  158.         keystring=kv["depots"][depotStr]
  159.         print("Depot key: %s" % keystring)
  160.     else:
  161.         print("No depot key for depot %s in VDF, assuming the depot is unencrypted." % depotStr)
  162.         keystring=""
  163.  
  164.     pos=fname.find("_disk")
  165.     if pos==-1:
  166.         raise Exception("Bad SIM name")
  167.  
  168.     baseName=fname[:pos]
  169.     disc=int(fname[pos+5:-4])
  170.     depot=int(depotStr)
  171.     extractDepot(baseName,disc,depot,keystring,outDir)
  172.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement