349 lines
13 KiB
PHP
349 lines
13 KiB
PHP
<?php
|
|
//http://arcemu.org/wiki/Logon_Process
|
|
/*
|
|
2014-08-02 23:44:18 Accepting connection from '37.24.148.225'
|
|
2014-08-02 23:44:18 [Auth] got data for cmd 0 recv length 47
|
|
2014-08-02 23:44:18 Entering _HandleLogonChallenge
|
|
2014-08-02 23:44:18 [AuthChallenge] got header, body is 0x2b bytes
|
|
2014-08-02 23:44:18 [AuthChallenge] got full packet, 0x2b bytes
|
|
2014-08-02 23:44:18 [AuthChallenge] name(13): 'ADMINISTRATOR'
|
|
2014-08-02 23:44:18 [AuthChallenge] Account 'ADMINISTRATOR' is not locked to ip
|
|
2014-08-02 23:44:18 database authentication values: v='092F9FB72C46AD5847B6166E8D6C0C2B36F416CDE6D277BA896DA56950A734B7' s='8DD0BD0A1E0C894E7B24A4264E50F1AE4456DA8C25195B1895D6DF1EBAB840F9'
|
|
2014-08-02 23:44:18 [AuthChallenge] account ADMINISTRATOR is using 'enGB' locale (0)
|
|
2014-08-02 23:44:18 [Auth] got data for cmd 1 recv length 75
|
|
2014-08-02 23:44:18 Entering _HandleLogonProof
|
|
2014-08-02 23:44:18 User 'ADMINISTRATOR' successfully authenticated
|
|
2014-08-02 23:44:18 [Auth] got data for cmd 16 recv length 5
|
|
2014-08-02 23:44:18 Entering _HandleRealmList
|
|
2014-08-02 23:44:18 Updating Realm List...
|
|
|
|
|
|
2014-08-03 00:29:16 Accepting connection from '127.0.0.1'
|
|
2014-08-03 00:29:16 [Auth] got data for cmd 0 recv length 47
|
|
2014-08-03 00:29:16 Entering _HandleLogonChallenge
|
|
2014-08-03 00:29:16 [AuthChallenge] got header, body is 0x2b bytes
|
|
2014-08-03 00:29:16 [AuthChallenge] got full packet, 0x2b bytes
|
|
2014-08-03 00:29:16 [AuthChallenge] name(13): 'ADMINISTRATOR'
|
|
2014-08-03 00:29:16 [AuthChallenge] Account 'ADMINISTRATOR' is not locked to ip
|
|
2014-08-03 00:29:16 database authentication values: v='092F9FB72C46AD5847B6166E8D6C0C2B36F416CDE6D277BA896DA56950A734B7' s='8DD0BD0A1E0C894E7B24A4264E50F1AE4456DA8C25195B1895D6DF1EBAB840F9'
|
|
2014-08-03 00:29:16 [AuthChallenge] account ADMINISTRATOR is using 'enUS' locale (0)
|
|
*/
|
|
class client {
|
|
|
|
//http://arcemu.org/wiki/Client_Logon_Challenge
|
|
/*
|
|
uint8 cmd;
|
|
uint8 error;
|
|
uint16 size;
|
|
uint8 gamename[4];
|
|
uint8 version1;
|
|
uint8 version2;
|
|
uint8 version3;
|
|
uint16 build;
|
|
uint8 platform[4];
|
|
uint8 os[4];
|
|
uint8 country[4];
|
|
uint32 timezone_bias;
|
|
uint32 ip;
|
|
uint8 I_len;
|
|
uint8 I[50];
|
|
*/
|
|
private static function logon_challenge(){
|
|
return pack('HHvHHHHHHHvHHHHHHHHCCCChhhhhhhhh', //'H21L4H9',
|
|
AuthCmd::AUTH_LOGON_CHALLENGE,//'00',//.
|
|
0x06,//error
|
|
43,//size
|
|
dechex(ord('W')),dechex(ord('o')),dechex(ord('W')),'00', //WoW\0
|
|
0x02,0x0C,0x01, //V 1.12.1
|
|
5875,// build 5875
|
|
dechex(ord('6')),dechex(ord('8')),dechex(ord('x')),'00', //x86\0
|
|
dechex(ord('n')),dechex(ord('i')),dechex(ord('W')),'00', //Win\0
|
|
ord('S'),ord('U'),ord('n'),ord('e'), //enUS
|
|
'3C','00','00','00', //60 timezone
|
|
'01','00','00','7F', //ip
|
|
dechex(13)).// '0D').//accname length
|
|
'ADMINISTRATOR';
|
|
}
|
|
|
|
//http://arcemu.org/wiki/Server_Logon_Challenge
|
|
/*
|
|
uint8 cmd;
|
|
uint8 error;
|
|
uint8 unk2;
|
|
uint8 B[32];
|
|
uint8 g_len;
|
|
uint8 g;
|
|
uint8 N_len;
|
|
uint8 N[32];
|
|
uint8 s[32];
|
|
uint8 unk3[16];
|
|
uint8 unk4;
|
|
*/
|
|
private static function server_logon_challenge($data){
|
|
return unpack('hcmd/herror/hunk1/h32B/hglen/hg/hNlen/h32N/h32S/h16salt/hunk2', $data);}
|
|
|
|
/*
|
|
uint8 cmd;
|
|
uint8 A[32];
|
|
uint8 M1[20];
|
|
uint8 crc_hash[20];
|
|
uint8 number_of_keys;
|
|
uint8 unk;
|
|
*/
|
|
private static function logon_proof($N,$B,$salt,$unk2){
|
|
/*
|
|
* bekannt: B,g,N,S
|
|
* gesucht: A, M1, crc
|
|
*
|
|
|
|
// now lets start calculating
|
|
BigNumber N,A,B,a,u,x,v,S,salt,unk1,g,k(3); // init BNs, default k to 3
|
|
user=stringToUpper( _accname );
|
|
_authstr=stringToUpper( user +":"+_accpass );
|
|
|
|
B.SetBinary(lc.B,32);
|
|
g.SetBinary(lc.g,lc.g_len);
|
|
N.SetBinary(lc.N,lc.N_len);
|
|
salt.SetBinary(lc.salt,32);
|
|
unk1.SetBinary(lc.unk3,16);
|
|
|
|
logdebug("== My Bignums ==");
|
|
a.SetRand(19*8);
|
|
ASSERT(a.AsDword() > 0);
|
|
logdebug("--> a=%s",a.AsHexStr());
|
|
Sha1Hash userhash,xhash,uhash;
|
|
userhash.UpdateData(_authstr);
|
|
userhash.Finalize();
|
|
xhash.UpdateData(salt.AsByteArray(),salt.GetNumBytes());
|
|
xhash.UpdateData(userhash.GetDigest(),userhash.GetLength());
|
|
xhash.Finalize();
|
|
x.SetBinary(xhash.GetDigest(),xhash.GetLength());
|
|
logdebug("--> x=%s",x.AsHexStr());
|
|
v=g.ModExp(x,N);
|
|
logdebug("--> v=%s",v.AsHexStr());
|
|
A=g.ModExp(a,N);
|
|
logdebug("--> A=%s",A.AsHexStr());
|
|
uhash.UpdateBigNumbers(&A, &B, NULL);
|
|
uhash.Finalize();
|
|
u.SetBinary(uhash.GetDigest(), 20);
|
|
logdebug("--> u=%s",u.AsHexStr());
|
|
S=(B - k*g.ModExp(x,N) ).ModExp((a + u * x),N);
|
|
logdebug("--> S=%s",S.AsHexStr());
|
|
ASSERT(S.AsDword() > 0);
|
|
|
|
|
|
// calc M1 & M2
|
|
unsigned int i=0;
|
|
char S1[16+1],S2[16+1]; // 32/2=16 :) +1 for \0
|
|
// split it into 2 seperate strings, interleaved
|
|
for(i=0;i<16;i++){
|
|
S1[i]=S.AsByteArray()[i*2];
|
|
S2[i]=S.AsByteArray()[i*2+1];
|
|
}
|
|
|
|
// hash each one:
|
|
Sha1Hash S1hash,S2hash;
|
|
S1hash.UpdateData((const uint8*)S1,16);
|
|
S1hash.Finalize();
|
|
S2hash.UpdateData((const uint8*)S2,16);
|
|
S2hash.Finalize();
|
|
// Re-combine them
|
|
char S_hash[40];
|
|
for(i=0;i<20;i++){
|
|
S_hash[i*2]=S1hash.GetDigest()[i];
|
|
S_hash[i*2+1]=S2hash.GetDigest()[i];
|
|
}
|
|
_key.SetBinary((uint8*)S_hash,40); // used later when authing to world
|
|
logdebug("--> SessionKey=%s",_key.AsHexStr());
|
|
|
|
char Ng_hash[20];
|
|
Sha1Hash userhash2,Nhash,ghash;
|
|
userhash2.UpdateData((const uint8*)user.c_str(),user.length());
|
|
userhash2.Finalize();
|
|
//printchex((char*)userhash2.GetDigest(),userhash2.GetLength(),true);
|
|
Nhash.UpdateBigNumbers(&N,NULL);
|
|
Nhash.Finalize();
|
|
ghash.UpdateBigNumbers(&g,NULL);
|
|
ghash.Finalize();
|
|
for(i=0;i<20;i++)Ng_hash[i] = Nhash.GetDigest()[i]^ghash.GetDigest()[i];
|
|
//printchex(Ng_hash,20,true);
|
|
|
|
BigNumber t_acc,t_Ng_hash;
|
|
t_acc.SetBinary((const uint8*)userhash2.GetDigest(),userhash2.GetLength());
|
|
t_Ng_hash.SetBinary((const uint8*)Ng_hash,20);
|
|
|
|
|
|
Sha1Hash M1hash,M2hash;
|
|
|
|
M1hash.UpdateBigNumbers(&t_Ng_hash,&t_acc,&salt,&A,&B,NULL);
|
|
M1hash.UpdateData((const uint8*)S_hash,40);
|
|
M1hash.Finalize();
|
|
|
|
M2hash.UpdateBigNumbers(&A,NULL);
|
|
M2hash.UpdateData((const uint8*)M1hash.GetDigest(),M1hash.GetLength());
|
|
M2hash.UpdateData((const uint8*)S_hash,40);
|
|
M2hash.Finalize();
|
|
|
|
logdebug("== Common Hashes ==");
|
|
logdebug("--> M1=%s",toHexDump(M1hash.GetDigest(),M1hash.GetLength(),false).c_str());
|
|
logdebug("--> M2=%s",toHexDump(M2hash.GetDigest(),M2hash.GetLength(),false).c_str());
|
|
|
|
// Calc CRC & CRC_hash
|
|
// i don't know yet how to calc it, so set it to zero
|
|
char crc_hash[20];
|
|
memset(crc_hash,0,20);
|
|
|
|
logdebug("--> CRC=%s",toHexDump((uint8*)crc_hash,20,false).c_str());
|
|
|
|
|
|
// now lets prepare the packet
|
|
ByteBuffer packet;
|
|
packet << (uint8)AUTH_LOGON_PROOF;
|
|
packet.append(A.AsByteArray(),A.GetNumBytes());
|
|
packet.append(M1hash.GetDigest(),M1hash.GetLength());
|
|
packet.append(crc_hash,20);
|
|
packet << (uint8)0; // number of keys = 0
|
|
packet << (uint8)0; // 1.11.x compatibility (needs one more 0)
|
|
|
|
A = g^a
|
|
B = kv + g^b
|
|
u = H(A, B)
|
|
x = H(s, p)
|
|
S = (B - kg^x) ^ (a + ux)
|
|
K = H(S)
|
|
M = H(H(N) xor H(g), H(I), s, A, B, K)
|
|
*/
|
|
|
|
$N = self::bchexdec($N);
|
|
$B = self::bchexdec($B);
|
|
$a = rand(1,19*8);
|
|
$x = 0;
|
|
$v = 0;
|
|
//$S = self::bchexdec($read['S']);
|
|
$salt = self::bchexdec($salt);
|
|
$unk2 = self::bchexdec($unk2);
|
|
$g = self::bchexdec($g);
|
|
$k = 3;// init BNs, default k to 3
|
|
$user = 'ADMINISTRATOR'; // upper
|
|
$user_pass = 'ADMINISTRATOR'; // upper
|
|
$auth_str = $user.":".$user_pass;
|
|
|
|
$userhash = sha1($auth_str);
|
|
$x = sha1($salt.$auth_str);
|
|
$v= bcpow($g, $x, $N);
|
|
$A = bcpow($g, $a, $N);
|
|
$u = sha1($A.$B);
|
|
$S= bcpow((bcsub($B, bcmul($k, bcpow($g, $x, $N)))),(bcadd($a, bcmul($u, $x))),$N); //(B - k*g.ModExp(x,N) ).ModExp((a + u * x),N);
|
|
|
|
// calc M1 & M2
|
|
$i = 0;
|
|
$S1 = '';
|
|
$S2 = '';
|
|
// split it into 2 seperate strings, interleaved
|
|
for($i=0;$i<16;$i++){
|
|
$S1 .= substr($S, ($i*2)*2,2);
|
|
$S2 .= substr($S, ($i*2+1)*2,2);
|
|
}
|
|
// hash each one:
|
|
$S1hash = sha1($S1);
|
|
$S2hash = sha1($S2);
|
|
// Re-combine them
|
|
$S_hash = '';
|
|
for($i=0;$i<20;$i++){
|
|
$S_hash .= substr($S1hash, $i*2,2);
|
|
$S_hash .= substr($S2hash, $i*2,2);
|
|
}
|
|
$key = $S_hash;// used later when authing to world(session key)
|
|
|
|
$userhash2 = sha1($user);
|
|
$Nhash = sha1($N);
|
|
$ghash = sha1($g);
|
|
$Ng_hash = '';
|
|
for($i=0;$i<20;$i++){
|
|
$Ng_hash .= hexdec(substr($Nhash, $i*2,2))^hexdec(substr($ghash, $i*2,2));};
|
|
|
|
$t_acc = $userhash2;
|
|
$t_Ng_hash = $Ng_hash;
|
|
|
|
$M1hash = sha1($t_Ng_hash.$t_acc.$salt.$A.$B);
|
|
$M2hash = sha1($M1hash.$S_hash);
|
|
|
|
$A_hex = self::bcdechex($A);
|
|
|
|
// Calc CRC & CRC_hash
|
|
// i don't know yet how to calc it, so set it to zero
|
|
//...
|
|
$res = array( 'N' => $N,
|
|
'A' => $A,
|
|
'B' => $B,
|
|
'g' => $g,
|
|
'a' => $a,
|
|
'u' => $u,
|
|
'x' => $x,
|
|
'v' => $v,
|
|
'S' => $S,
|
|
'salt' => $salt,
|
|
'unk2' => $unk2,
|
|
'user' => $user,
|
|
'user_pass' => $user_pass,
|
|
'auth_str' => $auth_str,
|
|
'A_hex' => $A_hex,
|
|
'M1hash' => $M1hash,
|
|
substr($A_hex,1,2), substr($A_hex,3,2),substr($A_hex,5,2),substr($A_hex,7,2),substr($A_hex,9,2),substr($A_hex,11,2),substr($A_hex,13,2),substr($A_hex,15,2),substr($A_hex,17,2),substr($A_hex,19,2),substr($A_hex,21,2),substr($A_hex,23,2),substr($A_hex,25,2),substr($A_hex,27,2),substr($A_hex,29,2),substr($A_hex,31,2),substr($A_hex,33,2),substr($A_hex,35,2),substr($A_hex,37,2),substr($A_hex,39,2),substr($A_hex,41,2),substr($A_hex,43,2),substr($A_hex,45,2),substr($A_hex,47,2),substr($A_hex,49,2),substr($A_hex,51,2),substr($A_hex,53,2),substr($A_hex,55,2),substr($A_hex,57,2),substr($A_hex,59,2),substr($A_hex,61,2),substr($A_hex,63,2));
|
|
new WARNING(print_r($res,true));
|
|
$out = pack('hh32h20hhhhhhhhhhhhhhhhhhhhhh', //'hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh',//'hh32h20hhhhhhhhhhhhhhhhhhhhhh',
|
|
AuthCmd::AUTH_LOGON_PROOF, //Command
|
|
$A_hex,//substr($A_hex,1,2), substr($A_hex,3,2),substr($A_hex,5,2),substr($A_hex,7,2),substr($A_hex,9,2),substr($A_hex,11,2),substr($A_hex,13,2),substr($A_hex,15,2),substr($A_hex,17,2),substr($A_hex,19,2),substr($A_hex,21,2),substr($A_hex,23,2),substr($A_hex,25,2),substr($A_hex,27,2),substr($A_hex,29,2),substr($A_hex,31,2),substr($A_hex,33,2),substr($A_hex,35,2),substr($A_hex,37,2),substr($A_hex,39,2),substr($A_hex,41,2),substr($A_hex,43,2),substr($A_hex,45,2),substr($A_hex,47,2),substr($A_hex,49,2),substr($A_hex,51,2),substr($A_hex,53,2),substr($A_hex,55,2),substr($A_hex,57,2),substr($A_hex,59,2),substr($A_hex,61,2),substr($A_hex,63,2), //A self::bcdechex($A), substr($A_hex,1,64),// $A_hex,//
|
|
$M1hash, //0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, //M1 $M1hash,//substr($M1hash,1,40),//$M1hash,//
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //CRC
|
|
0x00, //Keys
|
|
0x00 //unk
|
|
);
|
|
return $out;
|
|
}
|
|
|
|
public static function import($ip){
|
|
new INFO('Tryin to Connect to '.$ip);
|
|
$errno = $errstr = '';
|
|
$fp = fsockopen($ip , 3724, $errno, $errstr, 1000);
|
|
//socket_set_blocking( $fp, false );
|
|
if (!$fp) {
|
|
new ERROR("ERROR: $errno - $errstr");
|
|
} else {
|
|
new INFO('Connection established');
|
|
|
|
$out = self::logon_challenge();
|
|
new WARNING($out);
|
|
//new WARNING(bin2hex($out));
|
|
fwrite($fp, $out);
|
|
$read = fread($fp, 119);//stream_get_contents($fp); //fread($fp, 1);
|
|
$read = self::server_logon_challenge($read);
|
|
new WARNING(print_r($read,true));
|
|
$out = self::logon_proof($read['N'],$read['B'],$read['salt'],$read['unk2']);
|
|
new WARNING($out);
|
|
fwrite($fp, $out);
|
|
fclose($fp);
|
|
new WARNING("success");
|
|
}
|
|
}
|
|
|
|
private static function bchexdec($hex)
|
|
{
|
|
$dec = 0;
|
|
$len = strlen($hex);
|
|
for ($i = 1; $i <= $len; $i++) {
|
|
$dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
|
|
}
|
|
return $dec;
|
|
}
|
|
private static function bcdechex($dec) {
|
|
$last = bcmod($dec, 16);
|
|
$remain = bcdiv(bcsub($dec, $last), 16);
|
|
if($remain == 0) {
|
|
return dechex($last);
|
|
} else {
|
|
return self::bcdechex($remain).dechex($last);
|
|
}
|
|
}
|
|
}
|