tanghai 13 жил өмнө
parent
commit
5464c67a36

+ 75 - 34
CSharp/App/Modules/Robot/Protos/Messages.cs

@@ -5,12 +5,11 @@ namespace Robot.Protos
 {
 	public static class MessageOpcode
 	{
-		public const ushort CMSG_AUTHLOGONPERMIT = 800;
+		public const ushort CMSG_AUTH_LOGON_PERMIT = 800;
 		public const ushort CMSG_OTP_PASSWORD = 801;
 		public const ushort CMSG_PPC_PASSWORD = 802;
-		public const ushort CMSG_AUTHLOGONCHALLENGE = 803;
-		public const ushort CMSG_AUTHLOGONCHALLENGE_2 = 804;
-		public const ushort CMSG_AUTHLOGONPROOF = 805;
+		public const ushort CMSG_AUTH_LOGON_CHALLENGE = 803;
+		public const ushort CMSG_AUTH_LOGON_PROOF = 805;
 		public const ushort SMSG_AUTH_LOGON_CHALLENGE_RESPONSE = 900;
 		public const ushort SMSG_REALM_LOGON_RESPONSE = 901;
 		public const ushort SMSG_REALM_LIST = 902;
@@ -18,8 +17,57 @@ namespace Robot.Protos
 		public const ushort SMSG_PASSWORD_PROTECT_TYPE = 904;
 	}
 
+	public static class ErrorCode
+	{
+		public const int REALM_AUTH_SUCCESS = 0;
+		// Unable to connect
+		public const int REALM_AUTH_FAILURE = 1;
+		// Unable to connect
+		public const int REALM_AUTH_UNKNOWN1 = 2;
+		// This game> account has been closed and is no longer available for use.
+		// Please go to site>/banned.html for further information.
+		public const int REALM_AUTH_ACCOUNT_BANNED = 3;
+		// The information you have entered is not valid.
+		// Please check the spelling of the account name and password.
+		// If you need help in retrieving a lost or stolen password,
+		// see site> for more information
+		public const int REALM_AUTH_NO_MATCH = 4;
+		// The information you have entered is not valid.
+		// Please check the spelling of the account name and password.
+		// If you need help in retrieving a lost or stolen password,
+		// see site> for more information
+		public const int REALM_AUTH_UNKNOWN2 = 5;
+		// This account is already logged into game.
+		// Please check the spelling and try again.
+		public const int REALM_AUTH_ACCOUNT_IN_USE = 6;
+		// You have used up your prepaid time for this account.
+		// Please purchase more to continue playing
+		public const int REALM_AUTH_PREPAID_TIME_LIMIT = 7;
+		// Could not log in to game> at this time. Please try again later.
+		public const int REALM_AUTH_SERVER_FULL = 8;
+		// Unable to validate game version.
+		// This may be caused by file corruption or interference of another program.
+		// Please visit site for more information and possible solutions to this
+		// issue.
+		public const int REALM_AUTH_WRONG_BUILD_NUMBER = 9;
+		// Downloading
+		public const int REALM_AUTH_UPDATE_CLIENT = 10;
+		// Unable to connect
+		public const int REALM_AUTH_UNKNOWN3 = 11;
+		// This game account has been temporarily suspended.
+		// Please go to site further information.
+		public const int REALM_AUTH_ACCOUNT_FREEZED = 12;
+		// Unable to connect
+		public const int REALM_AUTH_UNKNOWN4 = 13;
+		// Connected.
+		public const int REALM_AUTH_UNKNOWN5 = 14;
+		// Access to this account has been blocked by parental controls.
+		// Your settings may be changed in your account preferences at site.
+		public const int REALM_AUTH_PARENTAL_CONTROL = 15;
+	}
+
 	[DataContract]
-	public class CMSG_AuthLogonPermit
+	public class CMSG_Auth_Logon_Permit
 	{
 		[DataMember(Order = 1, IsRequired = true)]
 		public string Account { get; set; }
@@ -28,13 +76,6 @@ namespace Robot.Protos
 		public string PasswordMd5 { get; set; }
 	}
 
-	[DataContract]
-	public class SMSG_Lock_For_Safe_Time
-	{
-		[DataMember(Order = 1, IsRequired = true)]
-		public uint Time { get; set; }
-	}
-
 	[DataContract]
 	public class SMSG_Password_Protect_Type
 	{
@@ -47,7 +88,7 @@ namespace Robot.Protos
 		[DataMember(Order = 3, IsRequired = true)]
 		public uint PasswordProtectType { get; set; }
 
-		[DataMember(Order = 4, IsRequired = false)]
+		[DataMember(Order = 4, IsRequired = true)]
 		public byte[] PpcCoordinate { get; set; }
 	}
 
@@ -65,42 +106,42 @@ namespace Robot.Protos
 	}
 
 	[DataContract]
-	public class CMSG_AuthLogonChallenge
+	public class CMSG_Auth_Logon_Challenge
+	{
+	}
+
+	[DataContract]
+	public class SMSG_Auth_Logon_Challenge_Response
 	{
 		[DataMember(Order = 1, IsRequired = true)]
-		public string GameName { get; set; }
+		public int ErrorCode { get; set; }
 
 		[DataMember(Order = 2, IsRequired = true)]
-		public uint Version1 { get; set; }
+		public byte[] B { get; set; }
 
 		[DataMember(Order = 3, IsRequired = true)]
-		public uint Version2 { get; set; }
+		public byte[] G { get; set; }
 
 		[DataMember(Order = 4, IsRequired = true)]
-		public uint Version3 { get; set; }
+		public byte[] N { get; set; }
 
 		[DataMember(Order = 5, IsRequired = true)]
-		public uint Build { get; set; }
+		public byte[] S { get; set; }
 
 		[DataMember(Order = 6, IsRequired = true)]
-		public uint Platform { get; set; }
+		public byte[] Unk3 { get; set; }
 
 		[DataMember(Order = 7, IsRequired = true)]
-		public uint OS { get; set; }
-
-		[DataMember(Order = 8, IsRequired = true)]
-		public uint Country { get; set; }
-
-		[DataMember(Order = 9, IsRequired = true)]
-		public uint TimeMapBias { get; set; }
-
-		[DataMember(Order = 10, IsRequired = true)]
-		public uint IP { get; set; }
+		public uint SecurityFlags { get; set; }
+	}
 
-		[DataMember(Order = 11, IsRequired = true)]
-		public byte[] Password { get; set; }
+	[DataContract]
+	public class CMSG_Auth_Logon_Proof
+	{
+		[DataMember(Order = 1, IsRequired = true)]
+		public byte[] A { get; set; }
 
-		[DataMember(Order = 12, IsRequired = true)]
-		public byte[] I { get; set; }
+		[DataMember(Order = 2, IsRequired = true)]
+		public byte[] M1 { get; set; }
 	}
 }

+ 1 - 0
CSharp/App/Modules/Robot/RealmInfo.cs

@@ -5,5 +5,6 @@ namespace Robot
 	public class RealmInfo
 	{
 		public SMSG_Password_Protect_Type SmsgPasswordProtectType { get; set; }
+		public SMSG_Auth_Logon_Challenge_Response SmsgAuthLogonChallengeResponse { get; set; }
 	}
 }

+ 81 - 31
CSharp/App/Modules/Robot/RealmSession.cs

@@ -4,7 +4,10 @@ using System.Net.Sockets;
 using System.Threading.Tasks;
 using Helper;
 using Log;
+using Org.BouncyCastle.Crypto.Agreement.Srp;
 using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities.Encoders;
 using Robot.Protos;
 
@@ -66,41 +69,57 @@ namespace Robot
 			await this.networkStream.WriteAsync(neworkBytes, 0, neworkBytes.Length);
 		}
 
-		public async Task<bool> Handle_CMSG_AuthLogonPermit_Response()
+		public async Task<SMSG_Password_Protect_Type> Handle_CMSG_AuthLogonPermit_Response()
 		{
 			var result = await this.RecvMessage();
 			ushort opcode = result.Item1;
 			byte[] message = result.Item2;
 
-			if (opcode == 0)
+			if (opcode != MessageOpcode.SMSG_PASSWORD_PROTECT_TYPE)
 			{
-				Logger.Trace("opcode == 0");
-				throw new RealmException("opcode == 0");
+				Logger.Trace("opcode: {0}", opcode);
+				throw new RealmException(string.Format("error opcode: {0}", opcode));
 			}
 
-			if (opcode == MessageOpcode.SMSG_LOCK_FOR_SAFE_TIME)
+			var smsgPasswordProtectType = 
+				ProtobufHelper.FromBytes<SMSG_Password_Protect_Type>(message);
+
+			Logger.Trace("message: {0}", JsonHelper.ToString(smsgPasswordProtectType));
+
+			if (smsgPasswordProtectType.Code != 200)
 			{
-				var smsgLockForSafeTime = ProtobufHelper.FromBytes<SMSG_Lock_For_Safe_Time>(message);
-				Logger.Trace("account lock time: {0}", smsgLockForSafeTime.Time);
-				return false;
+				throw new RealmException(string.Format(
+					"SMSG_Lock_For_Safe_Time: {0}", 
+					JsonHelper.ToString(smsgPasswordProtectType)));
 			}
 
-			if (opcode != MessageOpcode.SMSG_PASSWORD_PROTECT_TYPE)
+			return smsgPasswordProtectType;
+		}
+
+		public async Task<SMSG_Auth_Logon_Challenge_Response> 
+			Handle_SMSG_Auth_Logon_Challenge_Response()
+		{
+			var result = await this.RecvMessage();
+			ushort opcode = result.Item1;
+			byte[] message = result.Item2;
+
+			if (opcode != MessageOpcode.SMSG_AUTH_LOGON_CHALLENGE_RESPONSE)
 			{
-				throw new RealmException(string.Format("error opcode: {0}", opcode));
+				Logger.Trace("opcode: {0}", opcode);
 			}
 
-			var smsgPasswordProtectType = ProtobufHelper.FromBytes<SMSG_Password_Protect_Type>(message);
-			this.realmInfo.SmsgPasswordProtectType = smsgPasswordProtectType;
-
-			Logger.Trace("message: {0}", JsonHelper.ToString(smsgPasswordProtectType));
+			var smsgAuthLogonChallengeResponse =
+				ProtobufHelper.FromBytes<SMSG_Auth_Logon_Challenge_Response>(message);
 
-			if (smsgPasswordProtectType.Code != 200)
+			if (smsgAuthLogonChallengeResponse.ErrorCode != ErrorCode.REALM_AUTH_SUCCESS)
 			{
-				return false;
+				Logger.Trace("error code: {0}", smsgAuthLogonChallengeResponse.ErrorCode);
+				throw new RealmException(
+					string.Format("SMSG_Auth_Logon_Challenge_Response ErrorCode: {0}", 
+					JsonHelper.ToString(smsgAuthLogonChallengeResponse)));
 			}
 
-			return true;
+			return smsgAuthLogonChallengeResponse;
 		}
 
 		public async Task<Tuple<ushort, byte[]>> RecvMessage()
@@ -142,7 +161,7 @@ namespace Robot
 			return new Tuple<ushort, byte[]>(opcode, messageBytes);
 		}
 
-		public async Task<bool> Login(string account, string password)
+		public async void Login(string account, string password)
 		{
 			byte[] passwordBytes = password.ToByteArray();
 			var digest = new MD5Digest();
@@ -151,24 +170,55 @@ namespace Robot
 			digest.BlockUpdate(passwordBytes, 0, passwordBytes.Length);
 			digest.DoFinal(passwordMd5, 0);
 
-			var cmsgAuthLogonPermit = new CMSG_AuthLogonPermit
+			// 发送帐号和密码MD5
+			var cmsgAuthLogonPermit = new CMSG_Auth_Logon_Permit
 			{ 
 				Account = account, 
 				PasswordMd5 = Hex.ToHexString(passwordMd5) 
 			};
-
-			this.SendMessage(MessageOpcode.CMSG_AUTHLOGONPERMIT, cmsgAuthLogonPermit);
-
-			bool result = await this.Handle_CMSG_AuthLogonPermit_Response();
-
-			if (result == false)
+			this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_PERMIT, cmsgAuthLogonPermit);
+			await this.Handle_CMSG_AuthLogonPermit_Response();
+
+			// 这个消息已经没有作用,只用来保持原有的代码流程
+			var cmsgAuthLogonChallenge = new CMSG_Auth_Logon_Challenge();
+			this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_CHALLENGE, cmsgAuthLogonChallenge);
+			var smsgAuthLogonChallengeResponse = 
+				await this.Handle_SMSG_Auth_Logon_Challenge_Response();
+
+			// 以下是SRP6处理过程
+			var random = new SecureRandom();
+			var srp6Client = new Srp6Client();
+			var n = new BigInteger(1, smsgAuthLogonChallengeResponse.N);
+			var g = new BigInteger(1, smsgAuthLogonChallengeResponse.G);
+			var s = new BigInteger(1, smsgAuthLogonChallengeResponse.S);
+			var b = new BigInteger(1, smsgAuthLogonChallengeResponse.B);
+			srp6Client.Init(n, g, new Sha1Digest(), random);
+			BigInteger a = srp6Client.GenerateClientCredentials(
+				s.ToByteArray(), account.ToByteArray(), password.ToByteArray());
+			BigInteger clientS = srp6Client.CalculateSecret(b);
+
+			// 计算M1
+			var sha1Digest = new Sha1Digest();
+			var kBytes = new byte[sha1Digest.GetDigestSize()];
+			var clientSBytes = clientS.ToByteArray();
+			sha1Digest.BlockUpdate(clientSBytes, 0, clientSBytes.Length);
+			sha1Digest.DoFinal(kBytes, 0);
+
+			sha1Digest.Reset();
+			var m1 = new byte[sha1Digest.GetDigestSize()];
+			var aBytes = a.ToByteArray();
+			var bBytes = b.ToByteArray();
+			sha1Digest.BlockUpdate(aBytes, 0, aBytes.Length);
+			sha1Digest.BlockUpdate(bBytes, 0, bBytes.Length);
+			sha1Digest.BlockUpdate(kBytes, 0, kBytes.Length);
+			sha1Digest.DoFinal(m1, 0);
+
+			var cmsgAuthLogonProof = new CMSG_Auth_Logon_Proof
 			{
-				return false;
-			}
-
-			var cmsgAuthLogonChallenge = new CMSG_AuthLogonChallenge { };
-
-			return true;
+				A = a.ToByteArray(),
+				M1 = clientS.ToByteArray()
+			};
+			this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_PROOF, cmsgAuthLogonProof);
 		}
 	}
 }

+ 8 - 5
CSharp/App/Modules/Robot/RobotViewModel.cs

@@ -77,7 +77,7 @@ namespace Modules.Robot
 			this.clientHost.Dispose();
 		}
 
-		public async void Login(string account, string password)
+		public void Login(string account, string password)
 		{
 			//try
 			//{
@@ -99,15 +99,18 @@ namespace Modules.Robot
 			//}
 
 			var session = new RealmSession("192.168.11.95", 8888);
-			bool result = await session.Login(account, password);
 
-			if (result == false)
+			try
 			{
-				Logger.Debug("session login fail!");
+				session.Login(account, password);
+			}
+			catch (Exception e)
+			{
+				Logger.Trace("recv exception: {0}, {1}", e.Message, e.StackTrace);
 				return;
 			}
 
-			Logger.Debug("session login success!");
+			Logger.Trace("session login success!");
 		}
 	}
 }

+ 2 - 1
CSharp/CSharp.sln.DotSettings

@@ -47,7 +47,8 @@
 	<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/STICK_COMMENT/@EntryValue">False</s:Boolean>
 	<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
-	<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_INVOCATION_LPAR/@EntryValue">True</s:Boolean>
+	
+	
 	<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_FIRST_TYPE_PARAMETER_CONSTRAINT/@EntryValue">True</s:Boolean>
 	<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_FOR_STMT_HEADER_STYLE/@EntryValue">WRAP_IF_LONG</s:String>
 	<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">100</s:Int64>

+ 44 - 0
CSharp/Platform/Helper/CryptoHelper.cs

@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Helper
+{
+	public class CryptoHelper
+	{
+		public static byte[] SRP6CalculateM1(
+			BigInteger n, BigInteger g, BigInteger s, BigInteger b, string account, string password)
+		{
+			var random = new SecureRandom();
+
+			var srp6Client = new Srp6Client();
+			srp6Client.Init(n, g, new Sha1Digest(), random);
+			BigInteger a = srp6Client.GenerateClientCredentials(
+				s.ToByteArray(), account.ToByteArray(), password.ToByteArray());
+			BigInteger clientS = srp6Client.CalculateSecret(b);
+
+			// 计算M1
+			var sha1Digest = new Sha1Digest();
+			var kBytes = new byte[sha1Digest.GetDigestSize()];
+			var clientSBytes = clientS.ToByteArray();
+			sha1Digest.BlockUpdate(clientSBytes, 0, clientSBytes.Length);
+			sha1Digest.DoFinal(kBytes, 0);
+
+			sha1Digest.Reset();
+			var m1 = new byte[sha1Digest.GetDigestSize()];
+			var aBytes = a.ToByteArray();
+			var bBytes = b.ToByteArray();
+			sha1Digest.BlockUpdate(aBytes, 0, aBytes.Length);
+			sha1Digest.BlockUpdate(bBytes, 0, bBytes.Length);
+			sha1Digest.BlockUpdate(kBytes, 0, kBytes.Length);
+			sha1Digest.DoFinal(m1, 0);
+			return m1;
+		}
+	}
+}

+ 5 - 0
CSharp/Platform/Helper/Helper.csproj

@@ -32,6 +32,10 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9283, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\packages\BouncyCastle.1.7.0\lib\Net20\BouncyCastle.Crypto.dll</HintPath>
+    </Reference>
     <Reference Include="protobuf-net, Version=2.0.0.621, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\..\packages\protobuf-net.2.0.0.621\lib\net40\protobuf-net.dll</HintPath>
@@ -43,6 +47,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="ByteHelper.cs" />
+    <Compile Include="CryptoHelper.cs" />
     <Compile Include="JsonHelper.cs" />
     <Compile Include="LoaderHelper.cs" />
     <Compile Include="ProtobufHelper.cs" />

+ 1 - 0
CSharp/Platform/Helper/Packages.config

@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
+  <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
   <package id="protobuf-net" version="2.0.0.621" targetFramework="net45" />
 </packages>