关灯
开启左侧

魔兽世界私服网服务器端vb.net编写参考资料

  [复制链接]
admin实名认证 发表于 2016-4-20 22:45:09 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
 
魔兽世界私服网可以说是现在市面上最红火的一个网络游戏了,不过从游戏性还是耐玩性,都强过不少其他网游。现在的网游,很多一部分都存在着私服,但是这些大多是泄漏出来的官方服务器端,所以只需稍加修改就可以架设一个完美的私服。不过魔兽世界一直以来只有模拟器,这些模拟器都是不少编程和破解高手费尽心血破解魔兽客户端,研究封包慢慢写出来的服务器端。但是就算市面上最好的模拟器(要收费的)WoWEmu,模拟度也只能达到大概60%左右,存在不少bug。还有更重要的是,由于WoWEmu使用的是文本文件数据库,还有也许服务器端框架上的问题,即便是非常强劲的服务器,都只能带起很少的在线人数,还有就是这个模拟器还是收费的。现在还有另外几款WoW的模拟器,例如RunWoW,WoWWoW,等,有些甚至还是SQL版,不过这些服务器端的模拟度都不如WoWEmu,所以使用的人也很少。

国内现在应该有很多人有兴趣编写一个自己的魔兽服务器端,但是碍于缺乏资料,不知道该从何下手,网上的资料却又非常不全和不详细,能找得到的唯一能编译的代码是非常古老的WoWDaemon,但是这东西是针对WoW Beta的,所以对现在的WoW来说,没多少价值。网上还有流传着一些反编译WoWWoW出来的代码,但是这个代码无法编译,而且因为用的代码混淆器,要想从中得到一些自己想要的东西是非常费力的。所以本人现在想把我近段时间对魔兽世界的研究结果发布出来,希望能对大家有所帮助。

本篇文章中的所有例子和讲解都是以vb.net为基础的,对c#的读者问题应该也不大。使用c++的估计也只是关心相关算法,所以也没有太大问题。适用客户端为1.80-1.92

首先,我会跟大家讲解一下魔兽世界在登陆过程中的流程。首先,客户端会发送一个包含用户名和客户端版本,语言等信息的包给服务器端,然后服务器端验证版本是否符合,用户名是否存在,然后将用户名和密码通过一个算法转换成一串16进制数据,发给客户端当验证码。客户端这时会使用这个验证码,跟客户输入的用户名和密码通过运算得到另外一个验证数据,再连同随机产生的通讯密码一同发给服务器端,服务器端再通过通讯密码,和第一次发送的验证码再通过一番计算,如果跟这次客户端发过来的验证数据一模一样,则验证通过,然后把验证码记录下来,供游戏服务器验证客户端是否经过帐号验证使用。

下面我会跟大家讲各个过程的封包结构和算法

1、  第一个封包(客户端发送用户名和版本信息)

这个封包的结构如下:

Public Structure ClientLogonChallenge

        Dim cmd As MyWoW.LoginServer.Packet.LogonOpcode  '            // OP code = CMD_AUTH_LOGON_CHALLENGE

        Dim [error] As MyWoW.LoginServer.Packet.LogonErrorCode    '            // 0x02

        Dim size As UInt16 '              // size of the rest of packet, without this part

        Dim gamename() As Char    '  // "WoW"

        Dim version() As Byte '      // 0x01 08 00 -> (1.8.0)

        Dim build As UInt16 '             // 0x7F12 -> 4735

        Dim platform() As Char   '   // 68x\0 -> "x86"

        Dim os() As Char   '              // niW\0 -> "Win"

        Dim localization() As Char '// BGne -> 'enGB'

        Dim timezone_bias As UInt32 ' // 0x78 00 00 00

        Dim ip() As Byte '           // client ip: 0x7F 00 00 01

        Dim acclen As Byte '              // length of account name (without zero-char)

        Dim acc() As Char '          // upcased account name (without zero-char)

End Structure

其中cmd是登陆过程使用的Opcode(即命令代码),登陆过程的Opcode列表如下:

Public Enum LogonOpcode As Byte

        CMD_AUTH_LOGON_CHALLENGE = &H0  ' // client

        CMD_AUTH_LOGON_PROOF = &H1   ' // client

        CMD_AUTH_RECONNECT_CHALLENGE = &H2 ' // client

        CMD_AUTH_RECONNECT_PROOF = &H3  ' // client

        CMD_REALM_LIST = &H10    ' // client

        CMD_XFER_INITIATE = &H30   ' // client? from server

        CMD_XFER_DATA = &H31    ' // client? from server

        CMD_XFER_ACCEPT = &H32    ' // not official name, from client

        CMD_XFER_RESUME = &H33    ' // not official name, from client

        CMD_XFER_CANCEL = &H34    ' // not official name, from client



        '// unknown:

        CMD_GRUNT_AUTH_CHALLENGE = &H0   ' // server

        CMD_GRUNT_AUTH_VERIFY = &H2   '   // server

        CMD_GRUNT_CONN_PING = &H10   ' // server

        CMD_GRUNT_CONN_PONG = &H11   ' // server

        CMD_GRUNT_HELLO = &H20    ' // server

        CMD_GRUNT_PROVESESSION = &H21  ' // server

        CMD_GRUNT_KICK = &H24    ' // server

End Enum

error为错误代码,列表如下:

Public Enum LogonErrorCode As Byte

        '// LOGIN_STATE_FAILED:



        LOGIN_FAILED = 1 ' // 2, B, C, D // "Unable to connect"

        LOGIN_BANNED = 3 ' // "This World of Warcraft account has been closed and is no longer in service -- Please check the registered email address of this account for further information."; -- This is the error message players get when trying to log in with a banned account.

        LOGIN_UNKNOWN_ACCOUNT = 4 ' // 5 // "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 and account, see www.worldofwarcraft.com for more information.";

        LOGIN_ALREADYONLINE = 6 ' // "This account is already logged into World of Warcraft.  Please check the spelling and try again.";

        LOGIN_NOTIME = 7 ' // "You have used up your prepaid time for this account. Please purchase more to continue playing";

        LOGIN_DBBUSY = 8 ' // "Could not log in to World of Warcraft at this time.  Please try again later.";

        LOGIN_BADVERSION = 9 ' // "Unable to validate game version.  This may be caused by file corruption or the interference of another program.  Please visitwww.blizzard.com/support/wow/ for more information and possible solutions to this issue.";

        LOGIN_PARENTALCONTROL = &HF ' // "17"="LOGIN_PARENTALCONTROL" // "Access to this account has been blocked by parental controls.  Your settings may be changed in your account preferences at http://www.worldofwarcraft.com.";

        '// LOGIN_STATE_AUTHENTICATED:

        LOGIN_OK = 0 ' // E

        '// LOGIN_STATE_DOWNLOADFILE, LOGIN_OK

        LOGIN_DOWNLOADFILE = &HA ' // not official name

End Enum

size为无符号16位整数型,表示除了封包头部(命令码,错误码,和封包大小)之外的剩余封包大小

gamename一直都为WoW,不过4个字节长度,第一个字节为0

version表示客户端版本号,3字节,一个字节表示一个小节,比如1.8.0则为01 08 00

build是客户端的build号,16位无符号整数,如:7F12就是4735。

platform是系统信息,一般为“68x”即x86,4字节

os是操作系统,如niW则为Win,4字节

localization是客户端语言和区域信息,英语英国则为BGne,即enGB,4字节

timezone就是时区,没什么好解释,4字节

ip是客户端ip地址,4字节,如7F 00 00 1为127.0.0.001

acclen为帐号名长度,8位无符号整数,1字节

acc为用户输入的用户名,全部大写

其实这么多信息,真正重要的是版本号和用户名,其他处不处理看个人喜好。

服务器的响应封包结构如下:

Public Structure SServerLogonChallenge_Ok

        Dim cmd As Packet.LogonOpcode ';           // 0x00 -> OPcode = CMD_AUTH_LOGON_CHALLENGE ? CMD_GRUNT_AUTH_CHALLENGE

        Dim [error] As Packet.LogonErrorCode ' ;             // 0 -> ok = LOGIN_OK

        Dim unk1 As Byte '           // 0x00

        Dim B() As Byte  '           // calculated by server

        Dim g_len As Byte '          // 0x01

        Dim g As Byte '          // 0x07 (const)

        Dim N_len As Byte '          // 0x20

        Dim N As BigInteger  '            // const

        Dim salt() As Byte '         // random

        Dim unk2() As Byte '         //1: BA 79 D4 8D - BF FC BF AD - 8C B4 EC B3 - 75 C5 96 05

End Structure

其中cmd还是CMD_AUTH_LOGON_CHALLENGE,没变。error不管是否版本错误或者帐号不存在,都通通回复LOGIN_OK,unk1不知道有什么用,0即可。

下面讲解B的算法:

要算B,首先得生成几个其他得值,首先是N,这个是一个不变的常数,也许大家已经注意到了,N的类型是BigInteger,B和SALT都是转换成16进制的BigInteger,vb.net或者c#本身不支持这个数据类型,要实现这个就要用到一个附加的类。这个类我稍微修改了一下,这样可以适用于WoW的运算,点击这里可以下载到。N用16进制表示出来为:"894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7",长度是32字节。然后是salt,是一个随机生成的一个32字节长的整数。g为常数7。

现在开始计算部分:

首先,我们需要一个临时字符串,用来保存从帐号数据库里取得的正确的用户名和密码(例如,用户名abc,密码def,那么这个字符串为:”ABC:DEF”)。此外我们还需要另外一个Byte()类变量,比如Dim TempHash() As Byte,TempHash等于刚才那个临时字符串的sha1值。现在将tempHash反转(reserve)然后用他创建一个新的临时BigInteger变量x。

现在我们需要另外一些临时变量:tmp As BigInteger, v As BigInteger, k As New BigInteger,其中tmp等于N反转后生成的biginteger,k等于3,v=(x^g) mod tmp

(运算符都为vb运算符,^是指数,mod是求余数,c#或者c++请自行修改)

现在需要随机生成另外一个20字节长度的大整数:Dim b As BigInteger。注意,这里的b是个临时变量,并不是最后计算结果B。

现在从新给tmp赋值:tmp=(k * v + (b ^ g) mod tmp) mod tmp

计算结果B等于tmp的16进制表达方式然后反转。

封包最后一个变量,unk2,随机生成一个16字节长度大整数转换成16进制即可

下面是我写的处理第一个封包的代码,没优化,所以有些地方有点不合理,不过不影响使用:

Private Shared Function ProcessClientLogonChallenge(ByVal sock As MyWoW.LoginServer.Client, ByVal data As ClientLogonChallenge) As Boolean

            Dim senddata As Packet.PacketSendManager.Packet

            Dim sendpacket As SServerLogonChallenge_Ok

            Dim tick As Integer = GetTickCount

            Dim x As BigInteger, tmp As New BigInteger, temphashb() As Byte

            sock.Challenged = True

            sendpacket.error = LogonErrorCode.LOGIN_OK

            sock.PendingError = LogonErrorCode.LOGIN_OK

            If data.acclen >= 30 Then

                sock.PendingError = LogonErrorCode.LOGIN_UNKNOWN_ACCOUNT

                'Return True

            End If

            If Convert.ToInt16(data.build) < m_minbuild Or Convert.ToInt16(data.build) > m_maxbuild Then

                sock.PendingError = LogonErrorCode.LOGIN_BADVERSION

            End If

            Dim _id As String = data.acc

            Dim _pass As String = m_Database.GetItem("id='" & _id & "'", "password")

            If _pass Is Nothing Then sock.PendingError = LogonErrorCode.LOGIN_UNKNOWN_ACCOUNT

            Dim TempHash As SHA1

            sendpacket.cmd = LogonOpcode.CMD_AUTH_LOGON_CHALLENGE

            'If sock.PendingError <> LogonErrorCode.LOGIN_OK Then sock.PendingError = LogonErrorCode.LOGIN_OK

            'If sendpacket.error <> LogonErrorCode.LOGIN_OK Then sendpacket.error = LogonErrorCode.LOGIN_OK

            sendpacket.unk1 = 0

            sendpacket.N_len = 32

            sendpacket.N = New BigInteger("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7")

            sock.N = New BigInteger(sendpacket.N)

            sendpacket.salt = tmp.genRandom(8 * 32).getBytes

            sock.Salt = New BigInteger(sendpacket.salt)

            sendpacket.g_len = 1

            sendpacket.g = 7

            TempHash = SHA1.Create

            Dim temp As String = _id & ":" & _pass

            sock.Username = _id.ToUpper

            TempHash.TransformFinalBlock(Helper.strtobytes(temp.ToUpper), 0, temp.Length)

            temphashb = TempHash.Hash

            temphashb = Helper.CombinBytes(sendpacket.salt, temphashb)

            TempHash = SHA1.Create

            TempHash.TransformFinalBlock(temphashb, 0, temphashb.Length)

            temphashb = TempHash.Hash

            Array.Reverse(temphashb)

            x = New BigInteger(temphashb)

            temphashb = sendpacket.N.getBytes

            Array.Reverse(temphashb)

            tmp = New BigInteger(temphashb)

            Dim g As New BigInteger(sendpacket.g), v As BigInteger, k As New BigInteger(3)

            v = g.modPow(x, tmp)

            sock.v = New BigInteger(v)

            Dim tmp2 As BigInteger, b As BigInteger

            b = BigInteger.genRandom(20 * 8)

            sock.pb = New BigInteger(b)

            Dim bb() As Byte = b.getBytes

            Array.Reverse(bb)

            b = New BigInteger(bb)

            tmp2 = BigInteger.op_Multiply(k, v)

            tmp = g.modPow(b, tmp)

            tmp = BigInteger.op_Addition(tmp2, tmp)

            tmp = BigInteger.op_Modulus(tmp, New BigInteger(temphashb))

            temphashb = tmp.getBytes

            Array.Reverse(temphashb)

            sock.B = New BigInteger(temphashb)

            sendpacket.B = temphashb

            sendpacket.unk2 = BigInteger.genRandom(8 * 16).getBytes

            Dim datas() As Byte, br As New BinReader

            ReDim temphashb(0)

            temphashb(0) = sendpacket.cmd

            datas = temphashb

            temphashb(0) = sendpacket.error

            datas = Helper.CombinBytes(datas, temphashb)

            temphashb(0) = sendpacket.unk1

            datas = Helper.CombinBytes(datas, temphashb)

            datas = Helper.CombinBytes(datas, sendpacket.B)

            temphashb(0) = sendpacket.g_len

            datas = Helper.CombinBytes(datas, temphashb)

            temphashb(0) = sendpacket.g

            datas = Helper.CombinBytes(datas, temphashb)

            temphashb(0) = sendpacket.N_len

            datas = Helper.CombinBytes(datas, temphashb)

            datas = Helper.CombinBytes(datas, sendpacket.N.getBytes)

            datas = Helper.CombinBytes(datas, sendpacket.salt)

            datas = Helper.CombinBytes(datas, sendpacket.unk2)

            Dim packets As New Packet.PacketSendManager.Packet

            packets.sock = sock.CSocket

            packets.data = datas

            ConMsg("Challenge运算时间:" & GetTickCount - tick)

            m_SendMgr.AddPacket(packets)

            Return True
 
VIP介绍
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

  • 最佳新人

    注册账号后积极发帖的会员
  • 活跃会员

    经常参与各类话题的讨论,发帖内容较有主见
  • 热心会员

    经常帮助其他会员答疑
  • 推广达人

    积极宣传本站,为本站带来更多注册会员
  • 宣传达人

    积极宣传本站,为本站带来更多的用户访问量
  • 灌水之王

    经常在论坛发帖,且发帖量较大
  • 突出贡献

    长期对论坛的繁荣而不断努力,或多次提出建设性意见
  • 优秀版主

    活跃且尽责职守的版主
  • 荣誉管理

    曾经为论坛做出突出贡献目前已离职的版主
  • 论坛元老

    为论坛做出突出贡献的会员

0关注

5粉丝

3421帖子

排行榜
作者专栏

QQ交流群&&微信订阅号

QQ交流群

微信订阅号

吾爱尚玩资源基地永久域名:

Www.523Play.Com

在线管理员QQ:1589479632

邮箱:Email@523play.com

QQ交流群:558936238

Copyright   ©2015-2116  吾爱尚玩资源基地|523play.comPowered by©523Pplay.Com技术支持:吾爱尚玩资源基地