;;----------------------------------------------------------------------------
;; SASME.ASM - SASME.DLL (Subspace ASM Encryption)  Coconut emulator, may 2000
;;----------------------------------------------------------------------------
;;
;;            S U B S P A C E    P A C K E T    E N C R Y P T I O N
;;
;; This DLL must be called from Visual Basic only, because it is using the
;; string VB data type.
;;
;;----------------------------------------------------------------------------
;; SASME.DLL is using KERNEL32.DLL.
;;
;;----------------------------------------------------------------------------
;; Visual Basic declares:
;;
;;   Declare Sub SSInit Lib "sasme" (SSEncrTable As SSTable)
;;
;;   Declare Function SSEncrypt Lib "sasme" (SSEncrTable As SSTable, _
;;                                           StrToEncr As String, _
;;                                           ByVal StartPos as Integer) _
;;                                           As Long
;;
;;   Declare Function SSDecrypt Lib "sasme" (SSEncrTable As SSTable, _
;;                                           StrToDecr As String, _
;;                                           ByVal StartPos as Integer) _
;;                                           As Long
;;
;;   Declare Function SSPingValue Lib "sasme" () As Long
;;
;;   Declare Sub SSRandomize Lib "sasme" (ByVal Seed as Long)
;;
;;   Declare Function SSRnd Lib "sasme" () As Integer
;;
;;   Declare Function SSKey01 Lib "sasme" () As Long
;;
;;   Declare Function SSProc1 Lib "sasme" () As Long
;;
;;   Declare Sub SSProc2 Lib "sasme" (Packet03 As String)
;;
;;   Type SStable
;;       SSKey           as Long
;;       SSBuf(1 to 520) as Byte
;;   End Type
;;
;;----------------------------------------------------------------------------
;; Visual Basic calls (pseudo code):
;;
;; 1. Initialize SSTable structure:
;;   Public SSEncrTable as SSTable
;;
;;   SSEncrTable.SSKey = &h12345678 'server key
;;   Call SSInit(SSEncrTable)
;;
;; 2. Encrypt strings:
;;   Dim S as String
;;   Dim StartPos as Integer
;;
;;   S = Chr(0) & Chr(5) & ...   'This are 2 examples of strings to encr
;;   S = Chr(3) & Chr(0) & ...   '  ''
;;
;;   If Mid(S, 1, 2) = Chr(3) & Chr(0) Then
;;       StartPos = 2
;;   Else
;;       StartPos = 3
;;   End If
;;
;;   Select Case SSEncrypt(SSEncrTable, S, StartPos)
;;       Case 0 : SendTo AddrTo, S
;;       Case 1 : MsgBox "Encryption error 1: StartPos = 0"
;;       Case 2 : MsgBox "Encryption error 2: StartPos > Len(StrToEncr)"
;;       Case 3 : MsgBox "Encryption error 3: NumByToEncr > 520"
;;   End Select
;;
;; 3. Decrypt strings:
;;   Dim S as String
;;
;;   RecvFrom AddrFrom, S
;;
;;   Select Case SSDecrypt(SSEncrTable, S, 3)
;;       Case 0 : Call PacketHandler(S)
;;       Case 1 : MsgBox "Decryption error 1: StartPos = 0"
;;       Case 2 : MsgBox "Decryption error 2: StartPos > Len(StrToDecr)"
;;       Case 3 : MsgBox "Decryption error 3: NumByToDecr > 520"
;;   End Select
;;
;; 4. Ping the server (STREAM mode):
;;   Dim Packet as String
;;
;;   Packet = GetString(SSPingValue)
;;   send Socket%, Packet, len(Packet), Flags%
;;
;; 5. Initialize rnd seed:
;;   Dim Long1 as Long
;;
;;   Call SSRandomize(0)      'Randomize timer
;;   Call SSRandomize(25)     'Randomize 25
;;   Call SSrandomize(Long1)  'Randomize to Long1 value
;;
;; 6. Get random number, range (0-32767)
;;   Dim Int1 as Integer
;;
;;   Int1 = SSRnd   'To get a random number in range 0-32767
;;
;; 7. Get long key for packet 01.
;;   Call WinSockLib.UDPSend(WinSockLib.UDPMySock.Remote, Chr(0) + Chr(1) + GetString(SSKey01) + Chr(1) + Chr(0))
;;
;; 8. Get long value for bytes 2,3,4,5 of 03 position packet.
;;   Packet03$ = Chr(3) & Chr(0) & GetString(SSProc1) & ...
;;
;; 9. Get byte value for byte number 0A of 03 position packet.
;;   'The 0A byte is a checksum of the first 16H bytes of 03 Packet,
;;   'so u can only call this routine when u have composed the bytes from 0
;;   'to 15H of the 03 packet.
;;   'It is very important to put 0 at position 0A before calling this.
;;   'The routine will put the value in position 0A of 03 packet, so you only
;;   'have to call the routine and it stores the right value in the right
;;   'position.
;;   SSProc2 Packet03$
;;
;;----------------------------------------------------------------------------
.386
.model flat

option prologue:none
option epilogue:none
option dotname

;KERNEL32.DLL declares
extrn __imp__GetTickCount@0:dword	;kernel32.dll!GetTickCount

;-----------------------------------------------------------------------------
.DataSeg	segment

	;This data segment is going to be unique and shared by all programs
	;which are using the DLL at same time.
	;This is done with the "-section:.DataSeg,rws" directive for the linker.

align DWORD

	BufAux	byte	528 dup(0)	;Buffer used to encr/decr strings

.DataSeg	ends
;-----------------------------------------------------------------------------

;-----------------------------------------------------------------------------
.DataSeg2	segment

	;This data segment is going to be private for each proccess which is
	;using the DLL. It means the system loader will load a separate copy
	;of this segment for each proccess which uses the DLL.

align DWORD

	RndSeed	dword	0		;Seed for random generator SSRnd.

.DataSeg2	ends
;-----------------------------------------------------------------------------

.code

align DWORD

;-----------------------------------------------------------------------------
DLL_Entry  proc  stdcall public, instance:DWORD, reason:DWORD, reserved:DWORD
	;
	; If u want to define a language (english, spanish, etc) for the DLL,
	; or perform some initialization process when the DLL is loaded,
	; this is the place to do it. The 2 lines coded in this routine are
	; the minimum that this routine must do, just return a good response
	; to the caller (the caller is the system program loader), indicating
	; the DLL was successfully loaded.
	;
	mov	al, 1	;Must return something other than 0 in eax
	ret	12	;as a good response to the caller.
	;
DLL_Entry  endp
;-----------------------------------------------------------------------------

align DWORD

;-----------------------------------------------------------------------------
SSInit	proc	stdcall public, SSTablePtr:DWORD
	;
	push	eax			;Save registers
	push	ebx			;  ''
	push	ecx			;  ''
	push	edx			;  ''
	push	esi			;  ''
	push	edi			;  ''
	nrp	= 6*4			;Calculate offset of params on stack
	;
	;Set ptr's and get values into SSTable structure
	mov	ebx, [esp+nrp+4]	;ebx = SSTablePtr (ptr to SSTable struct)
	lea	edi, [ebx+4]		;edi = ptr to SSTable.SSBuf
	mov	ecx, [ebx]		;ecx = value of SSTable.SSKey
	;
	;Set loop counter value
	mov	esi, 104h		;esi = LoopCounter (SSBuf size=208h by)
	;
SSInit_LoopHead:
	mov	eax, 834E0B5Fh		;Calculations
	imul	ecx			;   ''
	add	edx, ecx		;   ''
	sar	edx, 10h		;   ''
	mov	eax, edx		;   ''
	shr	eax, 1Fh		;   ''
	add	edx, eax		;   ''
	lea	eax, [edx+8*edx]	;   ''
	shl	eax, 3			;   ''
	sub	eax, edx		;   ''
	lea	eax, [eax+4*eax]	;   ''
	shl	eax, 1			;   ''
	sub	eax, edx		;   ''
	shl	eax, 2			;   ''
	mov	ebx, eax		;   ''
	mov	eax, ecx		;   ''
	cdq				;   ''
	mov	ecx, 1F31Dh		;   ''
	idiv	ecx			;   ''
	imul	edx, 41A7h		;   ''
	sub	edx, ebx		;   ''
	add	edx, 7Bh		;   ''
	test	edx, edx		;   ''
	mov	ecx, edx		;   ''
	jg	@F			;   ''
	lea	ecx, [edx+7FFFFFFFh]	;   ''
@@:	mov	[edi], cx		;Save 2 by to SSBuf
	add	edi, 2			;Point to next position in SSBuf
	dec	esi			;LoopCounter = LoopCounter - 1
	jnz	SSInit_LoopHead		;End loop (loop until esi=0)
	;
	pop	edi			;Restore registers
	pop	esi			;  ''
	pop	edx			;  ''
	pop	ecx			;  ''
	pop	ebx			;  ''
	pop	eax			;  ''
	;
	ret	4			;Exit
	;
SSInit	endp
;-----------------------------------------------------------------------------

align DWORD

;-----------------------------------------------------------------------------
SSEncrypt  proc  stdcall public, SSTablePtr:DWORD, StrPtr:DWORD, StartPos:DWORD
	;
	push	ebx			;Save registers
	push	ecx			;  ''
	push	edx			;  ''
	push	esi			;  ''
	push	edi			;  ''
	push	ebp			;  ''
	nrp	= 6*4			;Calculate offset of params on stack
	;
	;Validate for error 1, StartPos = 0
	mov	eax, [esp+nrp+12]	;Load StartPos in eax
	cmp	eax, 0			;If StartPos = 0 Then Exit Sub
	je	SSEncr_Err1		;  ''
	;
	;Validate for error 2, StartPos > Len(Str)
	mov	esi, [esp+nrp+8]	;esi = StrPtr (ptr to Str to Encrypt)
	mov	esi, [esi]		;Point esi to Str
	mov	ecx, [esi-4]		;Load Len(Str) in ecx
	cmp	eax, ecx		;If StartPos > Len(Str) Then Exit Sub
	ja	SSEncr_Err2		;  ''
	;
	;Validate for error 3, NumByToEncr > 520
	dec	eax			;StartPos = StartPos - 1
	sub	ecx, eax		;ecx=NumByToEncr=Len(Str)-(StartPos-1)
	cmp	ecx, 520		;If NumByToEncr > 520 Then Exit Sub
	ja	SSEncr_Err3		;  ''
	;
	;Copy Str to encrypt (from StartPos to EndOfString) to BufAux
	add	esi, eax		;Point esi to StartPos into Str
	lea	edi, [BufAux]		;Point edi to BufAux
	push	ecx			;Save ecx (ecx = NumByToEncr)
	rep	movsb			;Copy the string from memory to memory
	pop	ecx			;Restore ecx
	;
	;Set ptr's and get values into SSTable structure
	mov	edx, [esp+nrp+4]	;edx = SSTablePtr (ptr to SSTable struct)
	lea	edi, [edx+4]		;edi = ptr to SSTable.SSBuf
	mov	ebp, [edx]		;ebp = value of SSTable.SSKey
	;
	;Point ebx to BufAux
	lea	ebx, [BufAux]		;ebx = ptr to BufAux
	;
	;Set loop counter value
	push	ecx			;Save ecx
	shr	ecx, 2			;ecx = LoopCounter = (NumByToEncr/4)+1
	inc	ecx			;  ''
	;
	;Encryption loop
@@:	mov	esi, [edi]		;Load 4 by of SSBuf in esi
	xor	esi, ebp		;SSBuf xor SSKey
	mov	ebp, [ebx]		;Load 4 by to encrypt (of BufAux) in ebp
	xor	ebp, esi		;4 by to encr xor (SSBuf xor SSKey)
	mov	[ebx], ebp		;Save 4 by encrypted to BufAux (and use them as next SSKey)
	add	edi, 4			;Point to next position in SSBuf
	add	ebx, 4			;Point to next position in BufAux
	loop	@B			;End loop (ecx=ecx-1,loop until ecx=0)
	;
	;Copy encrypted BufAux to Str (from StartPos to EndOfString)
	pop	ecx			;Restore ecx (ecx = NumByToEncr)
	mov	edi, [esp+nrp+8]	;edi = StrPtr (ptr to Str to encrypt)
	mov	edi, [edi]		;Point edi to Str
	add	edi, eax		;Point edi to StartPos into Str
	lea	esi, [BufAux]		;esi = ptr to BufAux
	rep	movsb			;Copy the string from memory to memory
	;
	xor	eax, eax		;Return value in eax: 0 = successful
	;
SSEncr_End:
	pop	ebp			;Restore registers
	pop	edi			;  ''
	pop	esi			;  ''
	pop	edx			;  ''
	pop	ecx			;  ''
	pop	ebx			;  ''
	;
	ret	12			;Exit
	;
SSEncr_Err1:
	mov	eax, 1			;Error 1: StartPos = 0
	jmp	SSEncr_End		;  ''
	;
SSEncr_Err2:
	mov	eax, 2			;Error 2: StartPos > Len(Str)
	jmp	SSEncr_End		;  ''
	;
SSEncr_Err3:
	mov	eax, 3			;Error 3: NumByToEncr > 520
	jmp	SSEncr_End		;  ''
	;
SSEncrypt	endp
;-----------------------------------------------------------------------------

align DWORD

;-----------------------------------------------------------------------------
SSDecrypt  proc  stdcall public, SSTablePtr:DWORD, StrPtr:DWORD, StartPos:DWORD
	;
	push	ebx			;Save registers
	push	ecx			;  ''
	push	edx			;  ''
	push	esi			;  ''
	push	edi			;  ''
	push	ebp			;  ''
	nrp	= 6*4			;Calculate offset of params on stack
	;
	;Validate for error 1, StartPos = 0
	mov	eax, [esp+nrp+12]	;Load StartPos in eax
	cmp	eax, 0			;If StartPos = 0 Then Exit Sub
	je	SSDecr_Err1		;  ''
	;
	;Validate for error 2, StartPos > Len(Str)
	mov	esi, [esp+nrp+8]	;esi = StrPtr (ptr to Str to Decrypt)
	mov	esi, [esi]		;Point esi to Str
	mov	ecx, [esi-4]		;Load Len(Str) in ecx
	cmp	eax, ecx		;If StartPos > Len(Str) Then Exit Sub
	ja	SSDecr_Err2		;  ''
	;
	;Validate for error 3, NumByToDecr > 520
	dec	eax			;StartPos = StartPos - 1
	sub	ecx, eax		;ecx=NumByToDecr=Len(Str)-(StartPos-1)
	cmp	ecx, 520		;If NumByToDecr > 520 Then Exit Sub
	ja	SSDecr_Err3		;  ''
	;
	;Copy Str to decrypt (from StartPos to EndOfString) to BufAux
	add	esi, eax		;Point esi to StartPos into Str
	lea	edi, [BufAux]		;Point edi to BufAux
	push	ecx			;Save ecx (ecx = NumByToDecr)
	rep	movsb			;Copy the string from memory to memory
	pop	ecx			;Restore ecx
	;
	;Set ptr's and get values into SSTable structure
	mov	edx, [esp+nrp+4]	;edx = SSTablePtr (ptr to SSTable struct)
	lea	edi, [edx+4]		;edi = ptr to SSTable.SSBuf
	mov	ebp, [edx]		;ebp = value of SSTable.SSKey
	;
	;Point ebx to BufAux
	lea	ebx, [BufAux]		;ebx = ptr to BufAux
	;
	;Set loop counter value
	push	ecx			;Save ecx
	shr	ecx, 2			;ecx = LoopCounter = (NumByToDecr/4)+1
	inc	ecx			;  ''
	;
	;Decryption loop
@@:	mov	esi, [edi]		;Load 4 by of SSBuf in esi
	mov	edx, [ebx]		;Load 4 by to decr (of BufAux) in edx
	xor	esi, ebp		;SSBuf xor SSKey
	xor	esi, edx		;(SSBuf xor SSKey) xor 4 by to decr
	mov	[ebx], esi		;Save 4 by decrypted in BufAux
	mov	ebp, edx		;Use 4 by to decr as next SSKey
	add	edi, 4			;Point to next position in SSBuf
	add	ebx, 4			;Point to next position in BufAux
	loop	@B			;End loop (ecx=ecx-1,loop until ecx=0)
	;
	;Copy decrypted BufAux to Str (from StartPos to EndOfString)
	pop	ecx			;Restore ecx (ecx = NumByToDecr)
	mov	edi, [esp+nrp+8]	;edi = StrPtr (ptr to Str to decrypt)
	mov	edi, [edi]		;Point edi to Str
	add	edi, eax		;Point edi to StartPos into Str
	lea	esi, [BufAux]		;esi = ptr to BufAux
	rep	movsb			;Copy the string from memory to memory
	;
	xor	eax, eax		;Return value in eax: 0 = successful
	;
SSDecr_End:
	pop	ebp			;Restore registers
	pop	edi			;  ''
	pop	esi			;  ''
	pop	edx			;  ''
	pop	ecx			;  ''
	pop	ebx			;  ''
	;
	ret	12			;Exit
	;
SSDecr_Err1:
	mov	eax, 1			;Error 1: StartPos = 0
	jmp	SSDecr_End		;  ''
	;
SSDecr_Err2:
	mov	eax, 2			;Error 2: StartPos > Len(Str)
	jmp	SSDecr_End		;  ''
	;
SSDecr_Err3:
	mov	eax, 3			;Error 3: NumByToDecr > 520
	jmp	SSDecr_End		;  ''
	;
SSDecrypt	endp
;-----------------------------------------------------------------------------

align DWORD

;-----------------------------------------------------------------------------
SSPingValue	proc	stdcall public
	;
	push	edx					;Save registers
	;
	;Compute long value (to ping the SS server with it)
	call	dword ptr [__imp__GetTickCount@0]	;eax = tick count
	mov	edx, eax				;Calculations
	mov	eax, 0CCCCCCCDh				;  ''
	mul	edx					;  ''
	shr	edx, 3					;  ''
	mov	eax, edx				;  '' (move return value to eax)
	;
	;Return value in eax
	;
	pop	edx					;Restore registers
	;
	ret						;Exit
	;
SSPingValue	endp
;-----------------------------------------------------------------------------

align DWORD

;-----------------------------------------------------------------------------
SSRandomize	proc	stdcall public, Seed:DWORD
	;
	push	eax			;Save registers
	push	edx			;  ''
	nrp	= 2*4			;Calculate offset of params on stack
	;
	;Check for the seed not to be 0
	mov	eax, [esp+nrp+4]	;Load seed value in eax
	cmp	eax, 0					;If eax = 0 Then
	jne	StoreSeed				;
	;						;    eax = tick count
	;Call kernel32!GetTickCount			;
	call	dword ptr [__imp__GetTickCount@0]	;End If
	;
StoreSeed:
	mov	RndSeed, eax		;Store eax in RndSeed
	;
	pop	edx			;Restore registers
	pop	eax			;  ''
	;
	ret	4			;Exit
	;
SSRandomize	endp
;-----------------------------------------------------------------------------

align DWORD

;-----------------------------------------------------------------------------
SSRnd	proc	stdcall public
	;
	push	ecx				;Save registers
	push	edx				;  ''
	;
	;Check for the seed not to be 0
	mov	eax, RndSeed			;Load seed value in eax
	cmp	eax, 0					;If eax = 0 Then
	jne	GetRnd					;
	;						;    eax = tick count
	;Call kernel32!GetTickCount			;
	call	dword ptr [__imp__GetTickCount@0]	;End If
	;
	;Compute pseudo-random number
GetRnd:	lea	ecx, [eax+2*eax]		;Calculations
	lea	edx, [eax+4*ecx]		;  ''
	shl	edx, 4				;  ''
	add	edx, eax			;  ''
	shl	edx, 8				;  ''
	sub	edx, eax			;  ''
	lea	eax, [eax+4*edx+269EC3h]	;  ''
	mov	RndSeed, eax			;  '' (Store seed for next number)
	sar	eax, 10				;  ''
	and	eax, 7FFFh			;  '' (ax = return value (0-7FFFh))
	;
	;Return pseudo-random number in eax/ax (0-7FFFh)
	;
	pop	edx				;Restore registers
	pop	ecx				;  ''
	;
	ret					;Exit
	;
SSRnd	endp
;-----------------------------------------------------------------------------

align DWORD

;-----------------------------------------------------------------------------
SSKey01	proc	stdcall public
	;
	push	ecx			;Save registers
	push	edx			;  ''
	push	esi			;  ''
	push	edi			;  ''
	;
	;Compute long value (to be used as key in packet 01 to server)
	call	dword ptr [__imp__GetTickCount@0]	;eax = tick count
	mov	ecx, eax		;Calculations
	mov	eax, 0CCCCCCCDh		;  ''
	mul	ecx			;  ''
	mov	edi, edx		;  ''
	shr	edi, 3			;  ''
	call	SSRnd			;  '' (Get random number 0-7FFF in eax)
	mov	esi, eax		;  ''
	shl	esi, 10h		;  ''
	call	SSRnd			;  '' (Get random number 0-7FFF in eax)
	add	edi, esi		;  ''
	add	eax, edi		;  ''
	cdq				;  ''
	xor	eax, edx		;  ''
	sub	eax, edx		;  ''
	neg	eax			;  ''
	;
	;Return 01 key in eax
	;
	pop	edi			;Restore registers
	pop	esi			;  ''
	pop	edx			;  ''
	pop	ecx			;  ''
	;
	ret				;Exit
	;
SSKey01	endp
;-----------------------------------------------------------------------------

align DWORD

;-----------------------------------------------------------------------------
SSProc1	proc	stdcall public
	;
	push	edx			;Save registers
	;
	;Compute long value (to be used in positions 2,3,4,5 of 03 packet
	call	dword ptr [__imp__GetTickCount@0]	;eax = tick count
	mov	edx, eax		;Calculations
	mov	eax, 0CCCCCCCDh		;  ''
	mul	edx			;  ''
	shr	edx, 3			;  ''
	add	edx, eax		;  ''
	and	edx, 7FFFFFFFh		;  ''
	;
	;Return value in eax
	mov	eax, edx		;eax = return value
	;
	pop	edx			;Restore registers
	;
	ret				;Exit
	;
SSProc1	endp
;-----------------------------------------------------------------------------

align DWORD

;-----------------------------------------------------------------------------
SSProc2	proc	stdcall public, SSPacket03Ptr:DWORD
	;
	push	ecx			;Save registers
	push	edx			;  ''
	nrp	= 2*4			;Calculate offset of params on stack
	;
	;Compute byte value (to be used in position 0A of 03 packet)
	mov	ecx, [esp+nrp+4]	;Load Ptr to SSPacket03 in ecx
	mov	ecx, [ecx]		; ''
	mov	edx, 16h		;Load LoopCounter value in edx (16h)
	xor	al, al			;al = 0
@@:	xor	al, [ecx]		;Loop head. al = al xor Packet03Byte
	inc	ecx			;Point ecx to next byte in packet 03
	dec	edx			;Loopcounter = LoopCounter - 1
	jnz	@B			;Loop foot. Loop Until LoopCounter = 0
	;
	mov	ecx, [esp+nrp+4]	;Load Ptr to SSPacket03 in ecx
	mov	ecx, [ecx]		;  ''
	add	ecx, 0Ah		;Point to 0A byte into 03 packet
	mov	[ecx], al		;Store the result (al) in 0A byte.
	;
	pop	edx			;Restore registers
	pop	ecx			;  ''
	;
	ret	4			;Exit
	;
SSProc2	endp
;-----------------------------------------------------------------------------

end
