Simutronics onsite team connection questions.
Closed this issue · 2 comments
BinuDR commented
Lyneya (Nora) has asked me to post this here and if anyone has any questions to forward to the onsite team, they can be sent to her through discord. Her discord handle is Lyneya#1483.
The service that we want to discontinue listens on port 7900 at eaccess.play.net. We probably have multiple domain names that point to that IP address. Information exchanged to that service is not protected to any modern standard of encryption.
The replacement service listens on the same IP address but port 7910. It uses the TLS protocol standard. The data sent over the encrypted channel is the same format as before except there are no newlines at the end of messages (we rely on TLS to deliver complete messages).
It is important that clients verify that the certificate received by the handshake is what we've configured on the server (to prevent man in the middle attacks). We use a self-signed certificate, so you can't follow the certificate chain to a trusted root certificate. In our Windows client, we include the certificate in a file that is read and compared with the received certificate.
That is the high level technical description of what needs to be done. Beyond that, we can try to answer questions and provide sample C++ code from what we do in the SGE.
vtcifer commented
I have sample code that should work/be enough to update our version of core lich, but I'm not in a position to be able to commit on my fork / submit the PR right now.
This is the POC code I used that spits out the response from the login server:
#!/usr/bin/env ruby
# encoding: US-ASCII
require 'socket'
require 'openssl'
acct = ''
pass = ''
gamecode = 'DR'
hostname = 'eaccess.play.net'
port = 7910
cert = OpenSSL::X509::Certificate.new("-----BEGIN CERTIFICATE-----\nMIIFUDCCAzigAwIBAgIJAP1LKTzYRs74MA0GCSqGSIb3DQEBCwUAMDwxCzAJBgNV\nBAYTAlVTMREwDwYDVQQIDAhNaXNzb3VyaTEaMBgGA1UECgwRU2ltdXRyb25pY3Mg\nQ29ycC4wIBcNMTgwNzE2MjIzNjMyWhgPMzAxNzExMTYyMjM2MzJaMDwxCzAJBgNV\nBAYTAlVTMREwDwYDVQQIDAhNaXNzb3VyaTEaMBgGA1UECgwRU2ltdXRyb25pY3Mg\nQ29ycC4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD3IDOqTKtc+RmW\nZkv3isBPtqKD8rcTyjWFLzIazYnN7ZGmRMGtBTFATx4ugy1WiLih9pUOo84W5M7i\nMwZlbzxQAUPiRfX3U7xowe5LuzDHSxqWzylkkgmkdCX9K8JoN4FnuIWUMuMji7f/\ncY7eL/Flob6pFMD9U7NZDvVpYH05Fnn0LAsaK0DKtPcms5+EWNh+uYgTjZbaVMyY\nAPTEN8Rh0NpR8CWNqErM6kbt+4NBqzKMIrJEfSSgQHhkXK+r37O+d3rpmgyFFJPl\nfFHlwxEKutdhd5z0MWV/Fj+hc2p0pExy6yDJNYDgI+iMas5aYowtOgHx2sX+pGkc\nCYgBkAyhAmhCja0Nl/TEbOgksnSvwGLWufc+TF89CZMGI7UmRdez/YKc0DR/zhjF\nAPsMur8wJOtps9ZueYqTqJ9SvwRoRT8Nlz0q/891t7P97rZ/+9C1AVJFtuIp3zM4\nGusqdxRqAsgVrgZmsY7FPti8dNTDcv1ZUiFt/FfHGEyJXbt7oYDr1CDAX17KRcc3\nFK9XaChz4VNlLGCYbCjMdPIqTP4M4xdma2bBMza4Nmr1qioDO27wa9zBiSe3Mskg\ntUm1cBmZ7eDfGprmMzg4FKY3WEBHQQINyuh9UNfqIijgAiPw4GN7jCpV3YH3mPRP\nHKlRSkfq8DxD5SXVV0+DyRslzLuDEQIDAQABo1MwUTAdBgNVHQ4EFgQUFNOI3jnn\nfZpGE12nVsE9QTgX2IQwHwYDVR0jBBgwFoAUFNOI3jnnfZpGE12nVsE9QTgX2IQw\nDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAPh0dOcZ4F2hQzwwr\nigLaY1p3fnjuGxnESwCPxJ3X+HVNclBh4Z3ndoonKunA+7CSxR9R4+ls/8RMmmr2\nQymzWYHTNbpe1dX+NgZGRTKiZEjqr8O0P02YEiLEUwG3YoWBSuka83LEDb+cB9AT\nUz90i1FMhGy9h+nBtP0r+mToQOnoKREmSoN03ucPVauionxAb7EqGlKciIyu4UE5\nuFY0kr5FqCIINtVIozyN2Xn/ATu6W5BlqET/PWapRa0230Pa6e3EKXVvjLxcMa2x\nyv+Pi/UQmMtpZXBu5qeYOPdppJs5WM33Q9PsCH7zGHziTbX85bhXIy1Y5TaGGjGK\nZZ/Je3zBn1JbyP+lC/DhRBpVwAbHqFCudxdSG4qcLR4r2hmTO8+LShXONmoH6XbR\nYwlb9aBrEYSr1cTrBnsFm07Bw3Ou9qXLfcF2nyT37U+DU8B9dcTll+Q1OPgYVbqG\nVfW7ZYQvvxb7EbXPcYedjGn1ZTGwJ5HRhJB0wNcH6wJIqw3y85hsqGFyw4zPOnDV\nZx2fEiycV6+6T8OIk2cwhZDcI0BI1iqKkRUdMLnVV/e3M9jERis1ValbeDVV+/8b\nLk71vz0A0lktJcULiMHWrym3IL7NTjuoZBJD8jgHETi4UEa0IB+Z7/Qr8F+UIygn\nAfksN019Wv0yPmHgubaJB2AT4ic=\n-----END CERTIFICATE-----")
cert_store = OpenSSL::X509::Store.new
ssl_context = OpenSSL::SSL::SSLContext.new
ssl_context.cert_store = cert_store
ssl_context.options = (OpenSSL::SSL::OP_NO_SSLv2 + OpenSSL::SSL::OP_NO_SSLv3)
cert_store.add_cert(cert)
login_socket = TCPSocket.open(hostname, port)
login_server = OpenSSL::SSL::SSLSocket.new(login_socket)
login_server.sync_close = true
login_server.connect
if login_server
puts login_server.state
login_server.puts "K\n"
hashkey = login_server.sysread(32)
puts hashkey
if 'test'[0].class == String
password = pass.split('').collect { |c| c.getbyte(0) }
hashkey = hashkey.split('').collect { |c| c.getbyte(0) }
else
password = pass.split('').collect { |c| c[0] }
hashkey = hashkey.split('').collect { |c| c[0] }
end
password.each_index { |i| password[i] = ((password[i]-32)^hashkey[i])+32 }
password = password.collect { |c| c.chr }.join
login_server.puts "A\t#{acct}\t#{password}\n"
password = nil
response = login_server.sysread(1500)
puts response
login_key = /KEY\t([^\t]+)\t/.match(response).captures.first
if login_key
login_server.puts "M\n"
response = login_server.sysread(1500)
puts response
if response =~ /^M\t/
login_server.puts "G\t#{gamecode}\n"
response = login_server.sysread(1500)
puts response
login_server.puts "P\t#{gamecode}\n"
response = login_server.sysread(1500)
puts response
login_server.puts "C\n"
response = login_server.sysread(1500)
puts response
#this response needs to be parsed to find the character you want to use. Format is tab separated:
#C <Used Slots> <Available Slots> ? ? W_<ACCT>_000 <CHARACTER0> W_<ACCT>_001 <CHARACTER1> W_<ACCT>_002 <CHARACTER2> etc
#I'm lazy and don't want to write the parsing code here so I've just hard coded it to get the first character
login_server.puts "L\tW_#{acct}_000\tSTORM\n"
response = login_server.sysread(1500)
puts response
else
login_server.close unless login_server.closed?
$stdout.puts "error: unrecognized response from server. (#{response})"
end
else
login_server.close unless login_server.closed?
$stdout.puts "Something went wrong... probably invalid user id and/or password.\nserver response: #{response}"
exit
end
else
$stdout.puts "error: failed to connect to server"
exit
end
KatoakDR commented
This is the POC code I used that spits out the response from the login server
Thanks!