Skip to content

Instantly share code, notes, and snippets.

@667bdrm
Created April 30, 2015 17:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 667bdrm/82018e7a5d8607bef337 to your computer and use it in GitHub Desktop.
Save 667bdrm/82018e7a5d8607bef337 to your computer and use it in GitHub Desktop.
CityGuide jam service emulator (incomplete)
#!/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