Advertisement
Guest User

Untitled

a guest
Jan 11th, 2015
712
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.43 KB | None | 0 0
  1. #This script will generate dat files that allow offline play of premium modules for the game Neverwinter Nights.
  2. #YOU MUST ONLY USE THIS SCRIPT TO GENERATE DATS FOR PREMIUM MODULES THAT YOU HAVE PURCHASED!
  3. import os, sys, socket, struct
  4. from binascii import unhexlify
  5.  
  6. try:
  7.     from Crypto.Cipher import Blowfish
  8. except ImportError:
  9.     print 'You must have PyCrypto installed to use this script.'
  10.     print "\nIf you're on Windows, the easiest way to get it is to grab an installer from:"
  11.     print 'http://www.voidspace.org.uk/python/modules.shtml#pycrypto'
  12.     sys.exit()
  13.  
  14.  
  15. #These are only part of the key used to decrypt the module.
  16. #The game generates the last 16 bytes of the key from the MD5 hash over the NWM file for the module.
  17. #Once you have the full key, you can decrypt the HAK with Blowfish in ECB mode, ignoring the first 8 bytes of the file.
  18.  
  19. #Edit: Removed the keys, the script just grabs them from the master server now.
  20. #But there should be enough info in this file to work out how to decrypt the dat and get them.
  21. modules = [
  22.     b"Neverwinter Nights - Kingmaker.dat",
  23.     b"Neverwinter Nights - ShadowGuard.dat",
  24.     b"Neverwinter Nights - Witch's Wake.dat",
  25.     b"Neverwinter Nights - Pirates of the Sword Coast.dat",
  26.     b"Neverwinter Nights - Wyvern Crown of Cormyr.dat",
  27.     b"Neverwinter Nights - Infinite Dungeons.dat"
  28.     ]
  29.  
  30. if len(sys.argv) < 2:
  31.     print 'Usage: %s *Key1*' % (os.path.basename(sys.argv[0]))
  32.     print 'Example: %s ABCDE-FGHIJ-KLMNO-PQRST-UVWXY-ZABCD-EFGHI' % (os.path.basename(sys.argv[0]))
  33.     print '\nTo find your CD key, look in "nwncdkey.ini" in the game folder.'
  34.     print 'You want the key listed as "Key1".'
  35.     print'\nYOU MUST ONLY USE THIS SCRIPT TO GENERATE DATS\nFOR PREMIUM MODULES THAT YOU HAVE PURCHASED!'
  36.     sys.exit()
  37.  
  38. cdKey = sys.argv[1].upper()
  39. cdKey = cdKey.replace('-','')
  40. if len(cdKey) != 35:
  41.     print 'The CD key(minus dashes) should be 35 characters long'
  42.     sys.exit()
  43. cdKey = cdKey + b'\x00' * (56-len(cdKey)) #Pad the CD key on the right with 0x00 to 56 bytes to get the encryption key for the dat.
  44.  
  45. cipher = Blowfish.new(cdKey, Blowfish.MODE_ECB)
  46.  
  47. print '\nModules:'
  48. for idx, module in enumerate(modules):
  49.     print '[%u] %s' % (idx+1, module[:-4])
  50. print '[q] quit'
  51. print'\nYOU MUST ONLY USE THIS SCRIPT TO GENERATE DATS\nFOR PREMIUM MODULES THAT YOU HAVE PURCHASED!\n'
  52.  
  53. sock1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  54. sock1.bind(('', 5120))
  55.  
  56. while(1):
  57.     inp = raw_input("Module choice: ")
  58.     try:
  59.         mnum = int(inp)-1
  60.     except ValueError:
  61.         if inp == 'q':
  62.             sys.exit()
  63.         continue
  64.     if (mnum < 0) or (mnum > 5):
  65.         continue
  66.    
  67.     #The master server currently lets any properly formatted auth request through, regardless of data.
  68.     reqpckt = 'BMDQ'
  69.     reqpckt += struct.pack('<H', 0x1400) #Port we're listening on
  70.     reqpckt += struct.pack('<H', len(modules[mnum][:-4])) #Length of module name
  71.     reqpckt += modules[mnum][:-4] #Module name
  72.     reqpckt += struct.pack('<H', 0x20)
  73.     reqpckt += b'\x00' * 0x20 #No clue what this is, but it's unchecked by the server currently.
  74.     reqpckt += struct.pack('<H', 0x01) #Number of times the next block will happen(the block consists of the 0x8 byte + 0x20 byte stuff)
  75.     reqpckt += struct.pack('<H', 0x08)
  76.     reqpckt += 'DEADBEA7' #Public CD key, unchecked by the server currently.
  77.     reqpckt += struct.pack('<H', 0x20)
  78.     reqpckt += b'\xFF' * 0x20 #Supposed to be some hash or something, unchecked by the server currently(but can't be 0x00..00).
  79.     reqpckt += struct.pack('<H', 0x44) #Size of public RSA key
  80.     reqpckt += struct.pack('<I', 0x01) #Exponent
  81.     reqpckt += b'\xFF' * 0x40 #Modulus
  82.     #Basically, they'll compute Msg^1 mod 0xFF...FF, which will just give us the original unencrypted message.
  83.     #That way, we don't have to deal with any RSA crap. :)
  84.  
  85.     print 'Sending auth request...'
  86.     sock1.sendto(reqpckt, ("nwmaster.bioware.com", 5121))
  87.     response, addr = sock1.recvfrom(2048)
  88.    
  89.     if response[:5] != b'BMDR\x01':
  90.         print 'Unexpected server response!'
  91.         continue
  92.    
  93.     datbuff = struct.pack('<B', 0x01) #Pointer to key data(just this 1 byte)
  94.     datbuff += response[0x21:0x21+0x28] #Key data
  95.     datbuff += b'\x00' * (1024-1-len(datbuff)) #Rest of the file can be whatever, it's not checked by the game.
  96.     datbuff += struct.pack('<B', 0x28) #Hardcoded check in the game for 0x28 here
  97.    
  98.     if len(datbuff) != 1024:
  99.         print 'Error'
  100.         sys.exit()
  101.    
  102.     with open(modules[mnum], 'wb') as fh:
  103.         fh.write(cipher.encrypt(datbuff)) #Encrypt the dat data and write it out.
  104.    
  105.     print 'Created "%s"!' % (modules[mnum])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement