Created
April 30, 2015 17:56
-
-
Save 667bdrm/82018e7a5d8607bef337 to your computer and use it in GitHub Desktop.
CityGuide jam service emulator (incomplete)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
use IO::Socket; | |
use IO::Socket::INET; | |
use Sys::Syslog; | |
use Sys::Syslog qw(:DEFAULT setlogsock); | |
use Sys::Syslog qw(:standard :macros); | |
use Time::Local; | |
setlogsock("console"); | |
openlog("pbotd", "cons,pid", LOG_USER); | |
my $sock = new IO::Socket::INET ( LocalHost => '0.0.0.0', LocalPort => '1961', Proto => 'tcp', Listen => 1, Reuse => 1 ); die "Could not create socket: $!\n" unless $sock; | |
while (my ($client,$clientaddr) = $sock->accept()) { | |
write_log("Connected from ".$client->peerhost()); | |
$pid = fork(); | |
die "Cannot fork: $!" unless defined($pid); | |
if ($pid == 0) { | |
# Child process | |
my $data = ''; | |
# Client protocol detection | |
$client->recv($data,4); | |
my $cproto = $data; | |
write_log($client->peerhost() . " proto = '$cproto'"); | |
if ($cproto =~ /DK(01|03|04|05)/) { | |
# generating crypto key | |
my $R = int(rand(255)); | |
$R = 1 if $R == 0; | |
write_log($client->peerhost() . " crypto key = $R (0x" . sprintf("%02x",$R) . ")"); | |
$client->send(pack('c',$R)); | |
# getting program signature | |
$client->recv($data,4); | |
# error count | |
my $errorcount = 0; | |
my $progsign = unpack('i',$data); | |
write_log($client->peerhost() . " program sign = ".$progsign . " (0x" . sprintf("%02x",$progsign) .")"); | |
$client->recv($data,3); | |
my $loginsign = $data; | |
# validating login signature | |
$errorcount++ if ($loginsign ne pack('ccc',$R^0xFF,$R^0xFF^0x01,$R^0xFF)); | |
write_log($client->peerhost() . " login sign = 0x".unpack('H*',$data)); | |
$client->recv($data,1); | |
my $loginlen = unpack('C',$data)^$R; | |
# validating login length | |
$errorcount++ if ($loginlen == 0); | |
write_log($client->peerhost() . " login length = $loginlen"); | |
$client->recv($data, $loginlen * 2); | |
my @logindata = unpack('C*',$data); | |
my $login = ''; | |
for (my $i=0 ; $logindata[$i] ; $i++) { | |
my $loginchar = $logindata[$i]^$R; | |
# validating login data | |
$errorcount++ if ($i % 2 == 1 and $logindata[$i] ne $R); | |
$login .= chr($loginchar) if $loginchar != 0; | |
} | |
write_log($client->peerhost() . " login = '$login'"); | |
$client->recv($data,3); | |
my $passsign = $data; | |
# validating password signature | |
$errorcount++ if ($passsign ne pack('ccc',$R^0xFF,$R^0xFF^0x01,$R^0xFF)); | |
write_log($client->peerhost() . " pass sign = 0x".unpack('H*',$data)); | |
$client->recv($data,1); | |
my $passlen = unpack('C',$data)^$R; | |
# validating password length | |
$errorcount++ if ($passlen == 0); | |
write_log($client->peerhost() . " pass length = $passlen"); | |
$client->recv($data, $passlen * 2); | |
my @passdata = unpack('C*',$data); | |
my $pass = ''; | |
for (my $i=0 ; $passdata[$i] ; $i++) { | |
my $passchar = $passdata[$i]^$R; | |
# validating password data | |
$errorcount++ if ($i % 2 == 1 and $passdata[$i] ne $R); | |
$pass .= chr($passchar) if $passchar != 0; | |
} | |
write_log($client->peerhost() . " pass = '$pass'"); | |
$client->recv($data,1); | |
my $unknown1 = unpack('C',$data)^$R; | |
write_log($client->peerhost() . " unknown1 = $unknown1 (0x".sprintf('%02x',$unknown1).")"); | |
$client->recv($data,1); | |
my $unknown2 = unpack('C',$data)^$R; | |
write_log($client->peerhost() . " unknown2 = $unknown2 (0x".sprintf('%02x',$unknown2).")"); | |
$client->recv($data,2); | |
my $logincheck = $data; | |
# final validating login packet | |
$errorcount++ if ($logincheck ne pack('cc',$R,$R)); | |
write_log($client->peerhost() . " login check = 0x".unpack('H*',$logincheck)); | |
write_log($client->peerhost() . " error counter = ".sprintf('%d',$errorcount)); | |
if ($login && $pass && $errorcount == 0) { | |
# login successful | |
$client->send(pack('S',0x1)); | |
write_log($client->peerhost() . " Client $login authenticated"); | |
# client command processing | |
while (1) { | |
my $mapname = ''; | |
if ($cproto =~ /DK0[3|4|5]/) { | |
$client->recv($data,5); | |
my($tmp,$mapnamelen)=unpack('iC', $data); | |
write_log($client->peerhost() . " map name length = $mapnamelen"); | |
$client->recv($data, $mapnamelen * 2); | |
my @mapnamedata = unpack('C*',$data); | |
for my $mapnamechar (@mapnamedata) { | |
$mapname .= chr($mapnamechar) if $mapnamechar != 0; | |
} | |
write_log($client->peerhost() . " map name = $mapname"); | |
} | |
my $mapver = 0; | |
if ($cproto =~ /DK0[4|5]/) { | |
$client->recv($data,2); | |
$mapver = unpack('S',$data); | |
} | |
write_log($client->peerhost() . " map version = " . sprintf('%d', $mapver)); | |
### temp ### | |
my $updatesavailable = 1; | |
if ($updatesavailable) { | |
$client->send(pack('S',0x03)); #updates available | |
$client->recv($data,2); | |
my $clientcmd = unpack('S',$data); | |
#temp | |
my $updfilename = '78SPb.jam'; | |
#got start updates command | |
if ($clientcmd == 0x02) { | |
my $updfilesize = -s $updfilename; | |
# updsize = size(namelen1)+size(name1)+12 + 4 + updfilesize1 + size(namelen2)+size(name2)+ 12 + 4 + updfilesize2 ... | |
my $updsize = 1 + length($updfilename) + 12 + 4 + $updfilesize; | |
write_log($client->peerhost() . " update filesize = " . sprintf('%d', $updfilesize)); | |
# send summary updates size | |
$client->send(pack('I',$updsize)); | |
# send filename length | |
$client->send(pack('C',length($updfilename))); | |
# send update filename | |
$client->send(pack('a*',$updfilename)); | |
#send unknown 4-byte data | |
$client->send(pack('I',0x8000000a)); | |
my ($min,$hour,$mday,$mon,$year) = (gmtime(time))[1,2,3,4,5]; | |
my $timestamp = timelocal(31,$min,$hour,$mday,$mon,$year); | |
#send update timestamp | |
$client->send(pack('I',$timestamp)); | |
#send unknown 4-byte data | |
$client->send(pack('I',0x00000000)); | |
# send update file size | |
$client->send(pack('I',$updfilesize)); | |
#send update | |
open(UPDFILE, $updfilename); | |
while (sysread(UPDFILE, $data, 1)) { | |
$client->send(pack("C",unpack("C",$data))); | |
} | |
close(UPDFILE); | |
$client->recv($data,2); | |
$clientcmd = unpack('S',$data); | |
$client->send(pack('S',0x05)); #no updates found code | |
exit(0); | |
} | |
} else { | |
$client->send(pack('S',0x05)); #no updates found code | |
} | |
} | |
} else { | |
write_log($client->peerhost() . " Authentication failed for $login"); | |
$client->send(pack('S',0x7)); #auth failure code | |
} | |
} | |
exit(0); # Child process exits when it is done. | |
} # else 'tis the parent process, which goes back to accept() | |
} | |
close($sock); | |
sub write_log() { | |
#syslog('info', $_[0]); | |
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time()); | |
my $timestamp = sprintf("%02d.%02d.%4d %02d:%02d:%02d",$mday,$mon+1,$year+1900,$hour,$min,$sec); | |
print "$timestamp pbotd[] " . $_[0] ."\n"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment