71.网络游戏逆向分析与漏洞攻防-角色与怪物信息的更新-分析并利用角色与怪物创建的数据包

本文主要是介绍71.网络游戏逆向分析与漏洞攻防-角色与怪物信息的更新-分析并利用角色与怪物创建的数据包,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

如果看不懂、不知道现在做的什么,那就跟着做完看效果

现在的代码都是依据数据包来写的,如果看不懂代码,就说明没看懂数据包

内容参考于: 易道云信息技术研究院VIP课

上一个内容:70.整理与角色数据更新有关的数据

码云版本号:4342753fb065199c70927502adcab7e5e68d6dae

代码下载地址,在 titan 目录下,文件名为:titan-分析并利用角色与怪物创建的数据包.zip

链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg

提取码:q9n5

--来自百度网盘超级会员V4的分享

HOOK引擎,文件名为:黑兔sdk升级版.zip

链接:https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw

提取码:78h8

--来自百度网盘超级会员V4的分享

以 69.利用封包更新角色数据到结构体 它的代码为基础进行修改

上一个内容里,分析出了更新角色的数据包 和 附近角色附近怪物初始化的数据包,本次先把附近角色、附近怪物初始化的数据包定义好

下图是角色刚登录进游戏,然后可以看到地图里有7个怪物

经过上个内容知道30数据包是附近角色或怪物(NPC)的初始化信息,下图30后面跟着一个7,这说明30数据包里面存在多个角色或NPC的初始化信息

数据拆分,除了30后面的个数其余与28数据包一样,由于数据包很长一屏只能显示一个多点,其实它是有7个的,完整的数据包提交到码云了,名字为 30数据包.txt

GameOBJECT.h和GameOBJECT.cpp文件有修改,需要重新生成一次,修改的什么详情看GameAnly.cpp文件里的CreateObjectfiles函数

GameAnly.cpp文件的修改:修改了 CreateObjectfiles函数

#include "pch.h"
#include "GameAnly.h"
#include <iostream>
#include <fstream>#ifdef Anly
// 它会生成一个结构体,详情看效果图
void GameAnly::AnlyBuff(char* start, char* end, int MsgId, char index)
{CStringA txt;CStringA tmp;CString utmp;EnCode _coder;GBYTE* _bytecoder;GSHORT* _shortcoder;GINT* _intcoder;GFLOAT* _floatcoder;GDOUBLE* _doublecoder;GCHAR* _asccoder;GUTF16* _utfcoder;GINT64* _int64coder;while (start < end) {_coder.Init(start, index);CStringA _opname = data_desc[_coder.index][_coder.op].name;// _opname.MakeLower()是变为小写字母,会影响 _opname它的值// 所以又写了一边 data_desc[_coder.index][_coder.op].nametmp.Format("%s %s;//", data_desc[_coder.index][_coder.op].name, _opname.MakeLower().GetBuffer());txt = txt + tmp;if (_coder.index == 0) {switch (_coder.op){case 1:_shortcoder = (GSHORT*)&_coder;tmp.Format("%d\r\n", _shortcoder->value());txt = txt + tmp;break;case 2:_intcoder = (GINT*)&_coder;tmp.Format("%d\r\n", _intcoder->value());txt = txt + tmp;break;case 4:_floatcoder = (GFLOAT*)&_coder;tmp.Format("%f\r\n", _floatcoder->value());txt = txt + tmp;break;case 6:_bytecoder = (GBYTE*)&_coder;tmp.Format("%d\r\n", _bytecoder->value());txt = txt + tmp;break;case 7:_utfcoder = (GUTF16*)&_coder;utmp.Format(L"[%s]\r\n", _utfcoder->value());tmp = utmp;txt = txt + tmp;break;// 5号之前分析的忘记截图了,现在找不到它的数据包了,如果后面再见到05的时候再详细补充说明// 之前的分析05就是double类型case 5:_doublecoder = (GDOUBLE*)&_coder;tmp.Format("%lf\r\n", _doublecoder->value());txt = txt + tmp;break;case 8:case 3:_int64coder = (GINT64*)&_coder;tmp.Format("%lld\r\n", _int64coder->value());txt = txt + tmp;break;default:break;}}if (_coder.index == 1) {switch (_coder.op){case 1:_shortcoder = (GSHORT*)&_coder;tmp.Format("%d\r\n", _shortcoder->value());txt = txt + tmp;break;case 2:_intcoder = (GINT*)&_coder;tmp.Format("%d\r\n", _intcoder->value());txt = txt + tmp;break;case 4:_floatcoder = (GFLOAT*)&_coder;tmp.Format("%f\r\n", _floatcoder->value());txt = txt + tmp;break;case 6:_asccoder = (GCHAR*)&_coder;tmp.Format("%s\r\n", _asccoder->value());txt = txt + tmp;break;case 7:_utfcoder = (GUTF16*)&_coder;utmp.Format(L"[%s]\r\n", _utfcoder->value());tmp = utmp;txt = txt + tmp;break;case 5:_doublecoder = (GDOUBLE*)&_coder;tmp.Format("%lf\r\n", _doublecoder->value());txt = txt + tmp;break;case 8:case 3:_int64coder = (GINT64*)&_coder;tmp.Format("%lld\r\n", _int64coder->value());txt = txt + tmp;break;default:break;}}}anly->SendData(TTYPE::I_DIS, MsgId, txt.GetBuffer(), txt.GetAllocLength() + 1);
}
void GameAnly::AnlyData(char* start, char* end, int count, int MsgId, POBJ_DESC desc)
{int iProc = 0;int value;long long llvalue;float fvalue;double dbval;CStringA szTmp, _tmp, szTxt;CString wTmp;while ((iProc < count) && (start <end)) {short* index = (short*)start;int type = desc[index[0]].type;char* name = desc[index[0]].name;switch (type){case 0:AfxMessageBox(L"0号信息!"); break;case 1:value = ToChar(start);szTmp.Format("%s = %d", name, value);break;case 2:value = ToShort(start);szTmp.Format("%s = %d", name, value);break;case 3:value = ToInt(start);szTmp.Format("%s = %d", name, value);break;case 4:llvalue = ToLLong(start);szTmp.Format("%s = %lld", name, llvalue);break;case 5:fvalue = ToFloat(start);szTmp.Format("%s = %f", name, fvalue);break;case 6:dbval = ToDouble(start);szTmp.Format("%s = %lf", name, dbval);break;case 7:_tmp = ToAscii(start);szTmp.Format("%s = %s", name, _tmp.GetBuffer());break;case 8:wTmp = ToUniode(start);_tmp = wTmp;szTmp.Format("%s = %s", name, _tmp.GetBuffer());break;case 9:AfxMessageBox(L"9号数据出现"); return;default:break;}szTxt = szTxt + szTmp + "\r\n";iProc++;}anly->SendData(TTYPE::I_DIS, MsgId, szTxt.GetBuffer(), szTxt.GetAllocLength() + 1);//CStringA tmpA;//CStringA szTxt, szTmp;//szTmp.Format("id:%lld\r\n", head->lId);//szTxt = szTxt + szTmp;//szTmp.Format("x:%f h:%f y:%f\r\n", head->x, head->h, head->y);//szTxt = szTxt + szTmp;//char* buffStart = (char*)head + sizeof(NR_OBNJECT_INIT) - 2;//int icount = head->icount;//int iProc = 0;//while (iProc < icount) {//    short* type = (short*)buffStart;//    char* _name = ObjectTable[type[0]].name;//    int _type = ObjectTable[type[0]].type;//    char* _byte;//    short* _short;//    int* _int;//    float* _float;//    long long* _llong;//    double* _double;//    int lenth;//    CString _txt;//    /*//        1B 00 type[0] buffStart + 2;//        0C 00 00 00 CA 4E 5A 66 53 62 01 80 4E 86 00 00//        1D 00 type[0]//        00 00 48 42 buffStart + 2;//        01 00 type[0]//        02 buffStart + 2;//        02 00 type[0]//        01 buffStart + 2;//        03 00 2E 00 00 00 67 75 69 5C 42 47 5F 74 65 61 6D 5C 54 65 61 6D 52 6F 6C 65 5C 54 65 61 6D 72 6F 6C 65 5F 7A 71 5F 68 75 6D 46 5F 30 30 31 2E 50 4E 47 00//        04 00 01 00 00 00//        05 00 01 00 00 00//        06 00 01 00 00 00//        07 00 01 00 00 00//        08 00 00 B1 9E 00//    *///    buffStart = buffStart + 2;//    switch (_type)//    {//    case 0://        AfxMessageBox(L"0号信息!"); break;//    case 1://        _byte = buffStart;//        szTmp.Format("%s = %d", _name, _byte[0]);//        buffStart = buffStart + 1;//        break;//    case 2://        _short = (short*)buffStart;//        szTmp.Format("%s = %d", _name, _short[0]);//        buffStart = buffStart + 2;//        break;//    case 3://        _int = (int*)buffStart;//        szTmp.Format("%s = %d", _name, _int[0]);//        buffStart = buffStart + 4;//        break;//    case 4://        _llong = (long long*)buffStart;//        szTmp.Format("%s = %lld", _name, _llong[0]);//        buffStart = buffStart + 8;//        break;//    case 5://        _float = (float*)buffStart;//        szTmp.Format("%s = %f", _name, _float[0]);//        buffStart = buffStart + 4;//        break;//    case 6://        _double = (double*)buffStart;//        szTmp.Format("%s = %lf", _name, _double[0]);//        buffStart = buffStart + 8;//        break;//    case 7://        _int = (int*)buffStart;//        lenth = _int[0];//        // szTmp = buffStart + 4;//        szTmp.Format("%s = %s", _name, buffStart + 4);//        buffStart = buffStart + 4 + lenth;//        break;//    case 8://        _int = (int*)buffStart;//        lenth = _int[0];//        _txt = (wchar_t*)(buffStart + 4);//        tmpA = _txt;//        szTmp.Format("%s = %s", _name, tmpA);//        buffStart = buffStart + 4 + lenth;//        break;//    case 9:MessageBoxA(0, buffStart, buffStart, MB_OK); return true;//    default://        break;//    }//    szTxt = szTxt + szTmp + "\r\n";//    iProc++;//}//anly->SendData(TTYPE::I_DIS, S_OBJECT_INIT, szTxt.GetBuffer(), szTxt.GetAllocLength() + 1);
}
void GameAnly::CreateObjectfiles(POBJ_DESC desc, int icount)
{/*char* _GameOBJECThpp = "F:\\代码存放地\\c\\titan\\tilib\\GameOBJECT.h";char* _GameOBJECTcpp = "F:\\代码存放地\\c\\titan\\tilib\\GameOBJECT.cpp";char* _GameOBJECTdef = "F:\\代码存放地\\c\\titan\\tilib\\GameOBJECTDef.h";*/char* _GameOBJECThpp = "D:\\代码存放地\\c\\titan\\tilib\\GameOBJECT.h";char* _GameOBJECTcpp = "D:\\代码存放地\\c\\titan\\tilib\\GameOBJECT.cpp";char* _GameOBJECTdef = "D:\\代码存放地\\c\\titan\\tilib\\GameOBJECTDef.h";AfxMessageBox(L"案说法是的撒");std::ofstream ofs(_GameOBJECThpp); // 根据09数据包生成类的头文件std::ofstream ofCpp(_GameOBJECTcpp); // 根据09数据包生成类的cpp文件std::ofstream ofDef(_GameOBJECTdef);// 生成宏if (ofs.bad() || ofCpp.bad() || ofDef.bad()) {ofs.close();ofCpp.close();ofDef.close();return;}else{// 定义CPP文件头部ofCpp << "#include \"pch.h\"" << std::endl;ofCpp << "#include \"GameOBJECT.h\"" << std::endl;ofCpp << "#include \"GameOBJECTDef.h\"" << std::endl;ofCpp << "void GAMEOBJECT::UpdateData(int id, int type, char*& buffStart)" << std::endl;ofCpp << "{                                                              " << std::endl;ofCpp << "    /*                                                         " << std::endl;ofCpp << "        1B 00 buffStart                                        " << std::endl;ofCpp << "        0C 00 00 00 buffStart + 2                              " << std::endl;ofCpp << "        CA 4E 5A 66 53 62 01 80 4E 86 00 00 1D 00              " << std::endl;ofCpp << "    */                                                         " << std::endl;ofCpp << "    buffStart = buffStart + 2;" << std::endl;ofCpp << "    switch (id)" << std::endl;ofCpp << "    {" << std::endl;// 定义文件头部区域ofDef << "#pragma once" << std::endl;// 头部生成区域ofs << "#pragma once" << std::endl;ofs << "#define GASCII CStringA" << std::endl;ofs << "#define GUNICODE CString" << std::endl;ofs << "#define GOBJECT int" << std::endl;ofs << "typedef class GAMEOBJECT{" << std::endl;ofs << "public:" << std::endl;ofs << "    bool Isfree = false;" << std::endl;// 变量声明//  i = 1的原因是游戏的数据类型表里(接收的09数据包)第一个是NONEfor (int i = 1; i < icount; i++){char* valueName = desc[i].name;int valueType = desc[i].type;char* valueTypeName = data_desc[2][valueType].name;int valueSize = data_desc[2][valueType].lenth;ofs << "    " << valueTypeName << " " << valueName << ";" << std::endl;ofDef << "#define INDEX_" << valueName << " " << i << std::endl;ofCpp << "    case INDEX_" << valueName << ":" << std::endl;ofCpp << "      return Set" << valueName << "(buffStart);" << std::endl;}ofCpp << "    }" << std::endl;ofCpp << "}" << std::endl;// 函数声明ofs << "    void virtual UpdateData(int id, int type, char*& buffStart);" << std::endl;ofs << "protected:" << std::endl;for (int i = 1; i < icount; i++){char* valueName = desc[i].name;int valueType = desc[i].type;char* valueTypeName = data_desc[2][valueType].name;int valueSize = data_desc[2][valueType].lenth;ofs << "    void virtual Set" << valueName << "(char*& buffStart);" << std::endl;ofCpp << "void GAMEOBJECT::Set" << valueName << "(char*& buffStart)" << std::endl;ofCpp << "{" << std::endl;if(valueType == 7){ofCpp << "   int* lenth = (int*)buffStart;" << std::endl;ofCpp << "   buffStart += 4;" << std::endl;ofCpp << "   " << valueName << " = (char*)buffStart; " << std::endl;ofCpp << "   buffStart += lenth[0];" << std::endl;}else if(valueType == 8) {ofCpp << "   int* lenth = (int*)buffStart;" << std::endl;ofCpp << "   buffStart += 4;" << std::endl;ofCpp << "   " << valueName << " = (wchar_t*)buffStart; " << std::endl;ofCpp << "   buffStart += lenth[0];" << std::endl;}else {ofCpp << "    " << valueTypeName << "* value = (" << valueTypeName << "*)buffStart;" << std::endl;ofCpp << "    buffStart += sizeof(" << valueTypeName << ");" << std::endl;ofCpp << "    " << valueName << " = value[0];" << std::endl;}ofCpp << "}" << std::endl;}ofs << "}*PGAMEOBJ;" << std::endl;}ofs.close();ofCpp.close();ofDef.close();
}char GameAnly::ToChar(char*& start)
{char result = start[0];start = start + 3;return result;
}short GameAnly::ToShort(char*& start)
{short* result = (short*)(start + 2);start = start + 2 + 2;return result[0];
}int GameAnly::ToInt(char*& start)
{int* result = (int*)(start + 2);start = start + 2 + 4;return result[0];
}float GameAnly::ToFloat(char*& start)
{float* result = (float*)(start + 2);start = start + 2 + 4;return result[0];
}double GameAnly::ToDouble(char*& start)
{double* result = (double*)(start + 2);start = start + 2 + 8;return result[0];
}long long GameAnly::ToLLong(char*& start)
{long long* result = (long long*)(start + 2);start = start + 2 + 8;return result[0];
}char* GameAnly::ToAscii(char*& start)
{int* lenth = (int*)(start + 2);char* result = start + 2 + 4; // +4这个操作是跳过 lenth的值start = start + 2 + 4 + lenth[0];return result;
}wchar_t* GameAnly::ToUniode(char*& start)
{int* lenth = (int*)(start + 2);wchar_t* result = (wchar_t*)(start + 2 + 4); // +4这个操作是跳过 lenth的值start = start + 2 + 4 + lenth[0];return result;
}
#endif // Anly

NetClient.cpp文件的修改:新加 GetAimById函数、CreateAim函数、OnSvrObjectInitEx函数,修改了 Init函数、

#include "pch.h"
#include "NetClient.h"
#include "extern_all.h"bool NetClient::login(const char* Id, const char* Pass)
{const int bufflen = sizeof(DATA_LOGIN) + 1;char buff[bufflen];DATA_LOGIN data;// 有些操作系统这样写会报错,因为内存不对齐,现在Windows下没事//PDATALOGIN _data = (PDATALOGIN)(buff + 1);// 这样写就能解决内存对齐问题PDATALOGIN _data =&data;int len = strlen(Id);memcpy(_data->Id, Id, len);len = strlen(Pass);memcpy(_data->Pass, Pass, len);memcpy(buff+1, _data, sizeof(DATA_LOGIN));buff[0] = I_LOGIN;return  WinSock->OnSend(buff, sizeof(buff));}bool NetClient::DelRole(const wchar_t* rolename)
{PROLEDATA _role = GetRoleByName(rolename);if (_role == nullptr) {return false;}else {return DelRole(rolename, _role->name.lenth);}return false;
}bool NetClient::StartCreateRole()
{NET_CREATEROLE_START _data;return WinSock->OnSend(&_data.op, _data.len);
}bool NetClient::SelectCamp(const char* _campname)
{NET_SEND_BUFF _buff;NET_SEND_CHOOSECAMP _data;_data.opcode.Set(SC_CHOOSECAMP);_data.camps.Set(_campname);/* sizeof(_data) / sizeof(EnCode)的原因NET_SEND_CHOOSECAMP结构体里面,没别 东西全是 EnCode 这个结构*/short count = sizeof(_data) / sizeof(EnCode);_buff.count = count;/*CodeMe函数给 _buff.buff里写数据参数的数据也就是给0A开头数据包里写,数据参数个数后面的内容然后返回值是写了多长的数据也就是给0A开头数据包里的数据参数个数后面的数据写了多长*/int ilen = _data.CodeMe(count, _buff.buff);ilen = ilen + sizeof(NET_SEND_HEAD) - 1;return WinSock->OnSend(&_buff.op, ilen);
}bool NetClient::CreateRole(wchar_t* name, double sex, double camp, double face, double occu, const char* photo, const char* infos, const char* txt, double faceShape)
{// rolecount > 4说明角色的数量够了if (rolecount > 4)return false;int index = 0;bool roleindex[5]{true, true, true, true, true };for (int i = 0; i < rolecount; i++) {roleindex[roles[i].index] = false;}for (int i = 0; i < 5; i++){if (roleindex[i]) {index = i;break;}}// wchar_t _name[] = L"am52111";NS_CREATEROLE_HEAD_BUFF _buff;CREATE_ROLE_DATAS _data;_data.sex.Set(sex);_data.camp.Set(camp);_data.face.Set(face);_data.occu.Set(occu);_data.faceSahpe.Set(faceShape);//_data.Photo.Set("gui\BG_team\TeamRole\Teamrole_zq_humF_001.PNG");_data.Photo.Set(photo);//_data.Infos.Set("Face,0;Hat,0;Eyes,0;Beard,0;Ears,0;Tail,0;Finger,0;Cloth,0;Pants,0;Gloves,0;Shoes,0;Trait,0;HairColor,0;SkinColor,0;SkinMtl,0;Tattoo,0;TattooColor,16777215;");_data.Infos.Set(infos);_data.Txt.Set(txt);short count = sizeof(_data) / sizeof(EnCode);_buff.count = count;_buff.index = index;int lenth = wcslen(name) + 1;lenth = lenth * 2;memcpy(_buff.name, name, lenth);int ilen = _data.CodeMe(count, _buff.buff);ilen = ilen + sizeof(NET_SEHD_CREATEROLE_HEAD) - 3;return WinSock->OnSend(&_buff.op, ilen);
}bool NetClient::SelectRole(const wchar_t* rolename)
{PROLEDATA roles = GetRoleByName(rolename);if (roles == nullptr)return false;NS_SELECTROLE _data;memcpy(_data.buff, roles->name.value(), roles->name.lenth);return WinSock->OnSend((char*)&_data, sizeof(_data));
}PROLEDATA NetClient::GetRoleByName(const wchar_t* rolename)
{//PROLEDATA result = nullptr;for (int i = 0; i < rolecount; i++){// StrCmpW判断两个字符串是否相同// 比较时区分大小写,如果字符串相同返回0if (StrCmpW(roles[i].name.value(), rolename) == 0) {return &roles[i];}}return nullptr;
}bool NetClient::Talk(wchar_t* txt, int PdId, double un)
{NET_SEND_BUFF _buff;CHAT_PUBLIC _data;_data.opcode.Set(SC_CHAT);_data.ChartId.Set(PdId);_data.txt.Set(txt);_data.un.Set(un);/*sizeof(_data) / sizeof(EnCode)的原因NET_SEND_CHOOSECAMP结构体里面,没别 东西全是 EnCode 这个结构*/short count = sizeof(_data) / sizeof(EnCode);_buff.count = count;/*CodeMe函数给 _buff.buff里写数据参数的数据也就是给0A开头数据包里写,数据参数个数后面的内容然后返回值是写了多长的数据也就是给0A开头数据包里的数据参数个数后面的数据写了多长*/int ilen = _data.CodeMe(count, _buff.buff);ilen = ilen + sizeof(NET_SEND_HEAD) - 1;return WinSock->OnSend(&_buff.op, ilen);
}bool NetClient::TalkTo(wchar_t* name, wchar_t* txt, double un)
{NET_SEND_BUFF _buff;CHAT_PRIVATE _data;_data.opcode.Set(SC_CHAT);_data.ChartId.Set(3);_data.txt.Set(txt);_data.name.Set(name);_data.un.Set(un);/*sizeof(_data) / sizeof(EnCode)的原因NET_SEND_CHOOSECAMP结构体里面,没别 东西全是 EnCode 这个结构*/short count = sizeof(_data) / sizeof(EnCode);_buff.count = count;/*CodeMe函数给 _buff.buff里写数据参数的数据也就是给0A开头数据包里写,数据参数个数后面的内容然后返回值是写了多长的数据也就是给0A开头数据包里的数据参数个数后面的数据写了多长*/int ilen = _data.CodeMe(count, _buff.buff);ilen = ilen + sizeof(NET_SEND_HEAD) - 1;return WinSock->OnSend(&_buff.op, ilen);
}bool NetClient::HeartBeep()
{NET_SEND_BUFF _buff;HEART_BEEP _data;_data.opcode.Set(SC_BEEP);_data.tick.Set(3);/*sizeof(_data) / sizeof(EnCode)的原因NET_SEND_CHOOSECAMP结构体里面,没别 东西全是 EnCode 这个结构*/short count = sizeof(_data) / sizeof(EnCode);_buff.count = count;/*CodeMe函数给 _buff.buff里写数据参数的数据也就是给0A开头数据包里写,数据参数个数后面的内容然后返回值是写了多长的数据也就是给0A开头数据包里的数据参数个数后面的数据写了多长*/int ilen = _data.CodeMe(count, _buff.buff);ilen = ilen + sizeof(NET_SEND_HEAD) - 1;return WinSock->OnSend(&_buff.op, ilen);
}bool NetClient::HeartLoop()
{NET_SEND_BUFF _buff;HEART_LOOP _data;_data.opcode.Set(SC_LOOP);_data.tick.Set(GetTickCount64());_data.txt.Set("");/*sizeof(_data) / sizeof(EnCode)的原因NET_SEND_CHOOSECAMP结构体里面,没别 东西全是 EnCode 这个结构*/short count = sizeof(_data) / sizeof(EnCode);_buff.count = count;/*CodeMe函数给 _buff.buff里写数据参数的数据也就是给0A开头数据包里写,数据参数个数后面的内容然后返回值是写了多长的数据也就是给0A开头数据包里的数据参数个数后面的数据写了多长*/int ilen = _data.CodeMe(count, _buff.buff);ilen = ilen + sizeof(NET_SEND_HEAD) - 1;return WinSock->OnSend(&_buff.op, ilen);
}bool NetClient::Backtoroles()
{// 返回角色NET_SEND_BUFF _buff;NSR_CHEAD _data;_data.opcode.Set(SC_REONLINE);/*sizeof(_data) / sizeof(EnCode)的原因NET_SEND_CHOOSECAMP结构体里面,没别 东西全是 EnCode 这个结构*/short count = sizeof(_data) / sizeof(EnCode);_buff.count = count;/*CodeMe函数给 _buff.buff里写数据参数的数据也就是给0A开头数据包里写,数据参数个数后面的内容然后返回值是写了多长的数据也就是给0A开头数据包里的数据参数个数后面的数据写了多长*/int ilen = _data.CodeMe(count, _buff.buff);ilen = ilen + sizeof(NET_SEND_HEAD) - 1;return WinSock->OnSend(&_buff.op, ilen);
}bool NetClient::DelRole(const wchar_t* rolename, unsigned _len)
{DATA_DELROLE _data;_data.op = 0x06;_data.len = _len;memcpy(_data.buff, rolename, _len);return WinSock->OnSend((char*)&_data, sizeof(DATA_DELROLE) - 1);
}PAIM NetClient::GetAimById(long long lId)
{if (Player.lId == lId)return &Player;for (int i = 0; i < MAX_AIM; i++){if ((Aimlst[i] != nullptr) && (Aimlst[i]->lId == lId)) {return Aimlst[i];}}return CreateAim();
}PAIM NetClient::CreateAim()
{for (int i = 0; i < MAX_AIM; i++){if (Aimlst[i] == nullptr) {Aimlst[i] = new AIM();return Aimlst[i];}else if (Aimlst[i]->Isfree) {return Aimlst[i];}}return PAIM();
}void NetClient::Init(GameWinSock* _winSock)
{for (int i = 0; i < 0x100; i++) {SendProc[i] = &NetClient::DefaultProc;RecvProc[i] = &NetClient::DefaultProc;}this->WinSock = _winSock;// 注册登录数据包处理函数SendProc[I_LOGIN] = &NetClient::OnClientlogin;SendProc[I_CREATEROLE_START] = &NetClient::OnClientStartCreateRole;SendProc[I_DELROLE] = &NetClient::OnClientDelRole;SendProc[I_SEND_CUSTOM] = &NetClient::OnClientSendCustom;SendProc[I_CREATEROLE] = &NetClient::OnClientCreateRole;SendProc[I_SELECT_ROLE] = &NetClient::OnClientSelectRole;// 注册数据登录失败数据包处理函数RecvProc[S_TIPS] = &NetClient::OnSvrTips;RecvProc[S_LOGINOK] = &NetClient::OnSverStruct;RecvProc[S_CREATEROLE_START] = &NetClient::OnSvrStartCreateRole;RecvProc[S_NOTICE] = &NetClient::OnSverNotice;RecvProc[S_NOTICE_COM] = &NetClient::OnSverNotice;RecvProc[S_OBJECT] = &NetClient::OnSverObject;RecvProc[S_STRUCT] = &NetClient::OnSverStruct;RecvProc[S_OBJECT_INIT] = &NetClient::OnSvrObjectInit;RecvProc[S_OBJECT_INITEX] = &NetClient::OnSvrObjectInitEx;
}bool NetClient::OnDelRole(wchar_t* rolename, unsigned _len)
{// AfxMessageBox(rolename);return true;
}void NetClient::Onlogin(const char* Id, const char* Pass)
{/*const int bufflen = sizeof(DATA_LOGIN) + 1;char buff[bufflen];DATA_LOGIN data;// 有些操作系统这样写会报错,因为内存不对齐,现在Windows下没事//PDATALOGIN _data = (PDATALOGIN)(buff + 1);// 这样写就能解决内存对齐问题PDATALOGIN _data =&data;int len = strlen(Id);memcpy(_data->Id, Id, len);len = strlen(Pass);memcpy(_data->Pass, Pass, len);memcpy(buff+1, _data, sizeof(DATA_LOGIN));buff[0] = I_LOGIN;return  WinSock->OnSend(buff, sizeof(buff));*/
}bool NetClient::OnStartCreateRole(int code)
{return true;
}bool NetClient::OnCreateRole(PNS_CREATEROLE _header, PCREATE_ROLE_DATAS _body)
{return true;
}bool NetClient::OnSendCustom(PNET_SEND_CHEAD _coder, char*& buffer, unsigned& len)
{switch (_coder->opcode.value()){case SC_CHOOSECAMP:return OnChooseCamp((PNS_CHOOSECAMP)_coder);case SC_CHAT:return OnChat((PCHAT_DATA)_coder);case SC_BEEP:return OnHeartBeep((PHEART_BEEP)_coder);case SC_LOOP:return OnHeartLoop((PHEART_LOOP)_coder);case SC_INITED:// case SC_REONLINE:case SC_INIT_START:case SC_HAND:case SC_HAND_IN:return false;default:break;}return true;
}bool NetClient::OnSelectRole(wchar_t* rolename)
{//AfxMessageBox(rolename);return true;
}bool NetClient::OnChooseCamp(PNS_CHOOSECAMP _coder)
{PNS_CHOOSECAMP _p = (PNS_CHOOSECAMP)_coder;return true;
}bool NetClient::OnChat(PCHAT_DATA _coder)
{switch (_coder->ChartId){case 3:// 私聊return OnChatPrivate((PCHAT_PRV)_coder);case 1:// 附近频道case 2:// 区域频道case 6:// 公会频道case 9:// 阵营频道case 21:// 喊话频道return OnChatPublic((PCHAT_PUB)_coder);break;}return true;
}bool NetClient::OnChatPublic(PCHAT_PUB _coder)
{return true;
}bool NetClient::OnChatPrivate(PCHAT_PRV _coder)
{return true;
}bool NetClient::OnHeartBeep(PHEART_BEEP _coder)
{return true; // 返回false会拦截81心跳包不给服务端发送
}bool NetClient::OnHeartLoop(PHEART_LOOP _coder)
{return true; // 返回false会拦截SC_LOOP心跳包不给服务端发送
}bool NetClient::OnSvrChat(PCHAT_PRV _coder)
{//AfxMessageBox(_coder->name);//AfxMessageBox(_coder->txt);//switch (_coder->ChartId)//{//case 3:// 私聊//    return OnChatPrivate((PCHAT_PRV)_coder);//case 1:// 附近频道//case 2:// 区域频道//case 6:// 公会频道//case 9:// 阵营频道//case 21:// 喊话频道//    return OnChatPublic((PCHAT_PUB)_coder);//    break;//}return true;
}bool NetClient::Tips(int code)
{
#ifdef  AnlyCString txt;if (code == 51001) {txt = L"登陆失败,易道云通行证不存在!";}else if (code == 51002) {txt = L"登录失败,密码错误!";}else if (code == 21101) {txt = L"人物重名!";}else if (code == 21109) {txt = L"名字过长或包含非法字符!";}else {txt.Format(L"未知登录错误:%d", code);}anly->SendData(TTYPE::I_LOG, 0, txt.GetBuffer(), (txt.GetLength() + 1)*2);
#endifreturn true;
}void NetClient::loginok(ROLE_DATA* _roles, int count)
{logined = true;if(roles) delete[] roles;roles = _roles;rolecount = count;
}bool NetClient::OnScrStartCreateRole(short code, wchar_t* _txt)
{return true;
}bool NetClient::OnSvrNotice(PNET_SEND_CHEAD _coder, int count, char*& buffer, unsigned& len)
{if (_coder->msgHeader == "chat") {return OnSvrChat((PCHAT_PRV)_coder);}return true;
}bool NetClient::OnRecvData(char*& buff, unsigned& len)
{
#ifdef  Anlyanly->SendData(TTYPE::I_RECV, buff[0], buff, len);
#endifreturn (this->*RecvProc[buff[0]])(buff, len);
}bool NetClient::OnSendData(char*& buff, unsigned& len)
{
#ifdef  Anlyanly->SendData(TTYPE::I_SEND, buff[0], buff, len);
#endifreturn (this->*SendProc[buff[0]])(buff, len);
}bool NetClient::OnConnect(char*& ip, unsigned& port)
{
#ifdef  Anly// 长度24的原因,它是宽字节要,一个文字要2个字节,一共是10个文字加上结尾的0是11个// 所以 11 乘以2,然后再加2 anly->SendData(TTYPE::I_LOG, 0, L"服务器正在连接。。。", 24);
#endifreturn true;
}// 默认的数据处理函数
bool NetClient::DefaultProc(char*&, unsigned&)
{return true;
}// 复制过来的内容
bool NetClient::OnClientlogin(char*& buff, unsigned& len)
{PDATALOGIN _data = (PDATALOGIN)(buff + 1);char* _id = _data->Id;_data = (PDATALOGIN)(buff + 1 + _data->lenId - 0x10);char* _pass = _data->Pass;Onlogin(_id, _pass);/* 修改账号密码len = sizeof(DATA_LOGIN) + 1;buff = new char[len];DATA_LOGIN data;PDATALOGIN _data = &data;buff[0] = 0x2;CStringA _id = "";// 补充账号CStringA _pass = "";// 补充密码memcpy(_data->Id, _id.GetBuffer(), _id.GetLength());memcpy(_data->Pass, _pass.GetBuffer(), _pass.GetLength());memcpy(buff + 1, _data, len - 1);*//* 监控登录数据PDATALOGIN _data = (PDATALOGIN)buff;CStringA _id = _data->Id;_data = (PDATALOGIN)(buff + _data->lenId - 0x10);CStringA _pass = _data->Pass;CStringA _tmp;// 请求登录 账号[% s]密码[% s] 这个内容别人在逆向的时候就会看到// 所以这种东西需要自己搞个编码来代替它_tmp.Format("请求登录 账号[%s]密码[%s]", _id, _pass);
#ifdef  Anlyanly->SendData(TTYPE::I_DIS, 1, _tmp.GetBuffer(), _tmp.GetAllocLength());
#endif*//*返回false,游戏无法发送数据包原因看调用此此函数的位置 OnSend 函数(if (SendDealProc[buff[0]]((buff + 1), len - 1)))*/return true;
}bool NetClient::OnClientStartCreateRole(char*& buff, unsigned& len)
{// 申请进入创建角色界面int* code = (int*)&buff[1];return OnStartCreateRole(code[0]);
}bool NetClient::OnClientCreateRole(char*& buff, unsigned& len) {PNS_CREATEROLE head = (PNS_CREATEROLE)(buff - 3);int icount = head->count;if (icount < 1)return true;char* buffStart = (char*)head + sizeof(NET_SEHD_CREATEROLE_HEAD);
#ifdef AnlyGameAnlyer->AnlyBuff(buffStart, buff + len, buff[0], 1);// 翻译解析约定数据
#endif // AnlyEnCode codes[sizeof(CREATE_ROLE_DATAS) / sizeof(EnCode)]{};int stDecode = 0;while (stDecode < icount) {codes[stDecode++] = buffStart;}/*Client->OnCreateRole(head, (PCREATE_ROLE_DATAS)codes) 数据包传给虚函数如果想对发送创建角色数据包做些什么直接继承NetClient重写OnCreateRole函数既可以了*/return Client->OnCreateRole(head, (PCREATE_ROLE_DATAS)codes);// 返回false屏蔽05开头的数据包,也就是创建角色发送的数据包
}bool NetClient::OnClientSendCustom(char*& buff, unsigned& len) {PNET_SEND_HEAD head = (PNET_SEND_HEAD)(buff - 1);int icount = head->count;if (icount < 1)return true;char* buffStart = (char*)head + sizeof(NET_SEND_HEAD);if (buffStart[0] != 0x02) {#ifdef  Anlyif (icount < MAX_SEND_COUNT)anly->SendData(TTYPE::I_DIS, I_SEND_CUSTOM, "SEND_CUSTOM MAX_SEND_COUNT 内存解码器空间不足", 46);anly->SendData(TTYPE::I_DIS, I_SEND_CUSTOM, "SEND_CUSTOM 发现异常数据", 25);
#endifreturn true;}#ifdef  AnlyGameAnlyer->AnlyBuff(buffStart, buff + len, buff[0], 1);
#endifint stDecode = 0;EnCode codes[MAX_SEND_COUNT]{};while (stDecode < icount) {codes[stDecode++] = buffStart;}/*Client->OnSendCustom((PNET_SEND_CHEAD)codes, buff, len); 数据包传给虚函数如果想对发送数据的0A开头的据包做些什么直接继承NetClient重写OnSendCustom函数既可以了*/return Client->OnSendCustom((PNET_SEND_CHEAD)codes, buff, len);}bool NetClient::OnClientSelectRole(char*& buff, unsigned& len) {PNS_SELECTROLE p = (PNS_SELECTROLE)buff;return Client->OnSelectRole((wchar_t*)(p->buff));
}bool NetClient::OnClientDelRole(char*& buff, unsigned& len) {PDATADELROLE p = (PDATADELROLE)buff;return Client->OnDelRole((wchar_t*)(p->buff), p->len);// 返回值改为false将拦截发送的删除角色数据包// 详情看注册 OnDelRole 函数的位置,Init函数// return true;
}// 接收数据截取区bool NetClient::OnSvrTips(char*& buff, unsigned& len) {int* code = (int*)&buff[1];return Client->Tips(code[0]);
}bool NetClient::OnSvrloginOk(char*& buff, unsigned& len) {PDATALOGINOK _p = (PDATALOGINOK)&buff[1];ROLE_DATA* roleDatas = nullptr;if (_p->RoleCount > 0) {char* buffStart = buff + 1 + sizeof(DATA_LOGIN_OK);#ifdef AnlyGameAnlyer->AnlyBuff(buffStart, buff + len, buff[0]);
#endif // AnlyroleDatas = new ROLE_DATA[_p->RoleCount];for (int i = 0; i < _p->RoleCount; i++){roleDatas[i].byte.Init(buffStart, 0);roleDatas[i].index.Init(buffStart, 0);roleDatas[i].un1.Init(buffStart, 0);roleDatas[i].name.Init(buffStart, 0);roleDatas[i].infos.Init(buffStart, 0);roleDatas[i].un2.Init(buffStart, 0);roleDatas[i].un3.Init(buffStart, 0);}Client->loginok(roleDatas, _p->RoleCount);}return true;
}bool NetClient::OnSverObject(char*& buff, unsigned& len) {PNR_HEAD head = (PNR_HEAD)(buff - 1);//head->count;if (ObjectTable) {delete[] ObjectTable;}if (ObjectTxt) {delete[] ObjectTxt;}ObjectTable = new OBJECT_DESC[head->count];ObjectTxt = new char[len];memcpy(ObjectTxt, buff, len);// 这里怕 buff 的内容被游戏释放掉,后面我们用的时候没法用,所以把buff的内容复制到我们的变量里char* buffStart = ObjectTxt + sizeof(NR_NOTICE_HEAD)-1;
//#ifdef Anly
//    CStringA szTxtA;
//    CStringA szTmp;
//#endif // Anly
//#ifdef Anly
//    szTmp.Format("[%X]%s:%d\r\n", i, ObjectTable[i].name, ObjectTable[i].type);
//    szTxtA += szTmp;
//#endif // Anly
//#ifdef  Anly
//    anly->SendData(TTYPE::I_DIS, S_OBJECT, szTxtA.GetBuffer(), szTxtA.GetAllocLength() + 1);
//#endif // Anlyfor (int i = 0; i < head->count; i++){ObjectTable[i].name = buffStart;buffStart = buffStart + strlen(ObjectTable[i].name) + 1;ObjectTable[i].type = buffStart[0];buffStart++;}#ifdef AnlyGameAnlyer->CreateObjectfiles(ObjectTable, head->count);
#endif // Anlyreturn true;
}
bool NetClient::OnSverStruct(char*& buff, unsigned& len) {return true;
}
bool NetClient::OnSvrObjectInit(char*& buff, unsigned& len)
{/*00 00 00 00 00 00 00 为了内存对齐补充了7个0,也就是 un1[6] 和 len28 opCD 48 00 01 62 A7 DE 04 PNR_OBJINIT:lIdC1 AA FB C3 PNR_OBJINIT:x;3D FF 22 41 PNR_OBJINIT:h;D7 0B 4A 44 PNR_OBJINIT:y;52 B8 06 40 PNR_OBJINIT:face;C1 AA FB C3 PNR_OBJINIT:tx;3D FF 22 41 PNR_OBJINIT:th;D7 0B 4A 44 PNR_OBJINIT:ty;52 B8 06 40 PNR_OBJINIT:tface;00 00 00 00 PNR_OBJINIT:un2[0]00 00 00 00 PNR_OBJINIT:un2[1]00 00 00 00 PNR_OBJINIT:un2[2]00 00 00 00 PNR_OBJINIT:un2[3]00 00 00 00 PNR_OBJINIT:un2[4]61 00       PNR_OBJINIT:icount;1B 00 (char*)head + sizeof(NR_OBNJECT_INIT) - 2;也就是指向到了 PNR_OBJINIT:type 这个位置0C 00 00 00 CA 4E 5A 66 53 62 01 80 4E 86 00 00 1D 00*/// 初始化对象PNR_OBJINIT head = (PNR_OBJINIT)(buff - 7);char* buffStart = (char*)head + sizeof(NR_OBNJECT_INIT) - 2;int nStart = (int)&Player.lId;int nEnd = (int)&Player.endclass;memcpy(&Player.lId, &head->lId, nEnd - nStart);#ifdef  AnlyGameAnlyer->AnlyData(buffStart, buff + len, head->icount, S_OBJECT_INIT, ObjectTable);
#endifint iProc = 0;while (iProc < head->icount){short* index = (short*)buffStart;int type = ObjectTable[index[0]].type;Player.UpdateData(index[0], type, buffStart);iProc++;}return true;
}
bool NetClient::OnSvrObjectInitEx(char*& buff, unsigned& len)
{// 初始化对象PNR_OBJINITEX head = (PNR_OBJINITEX)(buff - 5);char* buffStart;/*int nStart = (int)&Player.lId;int nEnd = (int)&Player.endclass;memcpy(&Player.lId, &head->lId, nEnd - nStart);*/int iObjectCount = 0;short objCount = head->obj_count;short refCount = head->icount;while (iObjectCount < objCount) {buffStart = (char*)head + sizeof(NR_OBNJECT_INITEX) - 2;
#ifdef  AnlyGameAnlyer->AnlyData(buffStart, buff + len, refCount, S_OBJECT_INITEX, ObjectTable);
#endifiObjectCount++;}/*int iProc = 0;while (iProc < head->icount){short* index = (short*)buffStart;int type = ObjectTable[index[0]].type;Player.UpdateData(index[0], type, buffStart);iProc++;}*/return true;
}
/*OnSverrNotice函数处理的数据包格式如下1E 06 0006 11 00 00 00 70 6C 61 79 5F 70 6F 69 6E 74 5F 73 6F 75 6E 64 0006 01 00 00 00 0004 2C 92 87 C504 FA 03 BF 4204 33 14 BD 4502 00 00 00 001E 06 00 是 PNR_NOTICE_HEAD06 11 00 00 00 70 6C 61 79 5F 70 6F 69 6E 74 5F 73 6F 75 6E 64 00是一个EnCode06 01 00 00 00 00是一个EnCode04 2C 92 87 C5是一个EnCode04 FA 03 BF 42是一个EnCode04 33 14 BD 45是一个EnCode02 00 00 00 00是一个EnCode
*/
bool NetClient::OnSverNotice(char*& buff, unsigned& len) {PNR_NOTICE_HEAD head = (PNR_NOTICE_HEAD)(buff - 1);int icount = head->count;char* buffStart = (char*)head + sizeof(NR_NOTICE_HEAD);if (icount < 1) {return true;}if (icount > MAX_RECV_COUNT) {
#ifdef  Anlyanly->SendData(TTYPE::I_DIS, S_NOTICE, "S_NOTICE 解码器内存不足", 24);
#endifreturn true;}#ifdef  AnlyGameAnlyer->AnlyBuff(buffStart, buff + len, buff[0], 1);
#endifint stDecode = 0;EnCode codes[MAX_RECV_COUNT]{};while (stDecode < icount) {codes[stDecode++] = buffStart;}return Client->OnSvrNotice((PNET_SEND_CHEAD)codes, icount, buff, len);
}
bool NetClient::OnSvrStartCreateRole(char*& buff, unsigned& len) {short* _st = (short*)&buff[1];wchar_t* _txt = (wchar_t*)&buff[3];
#ifdef  AnlyCString txt;CStringA txtA;txt.Format(L"code:%d\r\n%s", _st[0], _txt);txtA = txt;//AfxMessageBox(txtA);anly->SendData(TTYPE::I_DIS, S_CREATEROLE_START, txtA.GetBuffer(), txt.GetAllocLength() + 1);
#endif/*Client->OnSendCustom((PNET_SEND_CHEAD)codes, buff, len); 数据包传给虚函数如果想对0A开头的据包做些什么直接继承NetClient重写OnSendCustom函数既可以了*/return Client->OnScrStartCreateRole(_st[0], _txt);
}

NetClient.h文件的修改:新加 MAX_AIM宏、Aimlst变量、OnSvrObjectInitEx函数、GetAimById函数、CreateAim函数

#pragma once
#include "NetClass.h"
#include "GameWinSock.h"
#include "AIM.h"#define CAMP_NAME_QH "xuanrenQH"
#define CAMP_NAME_ZE "xuanrenZQ"#define MAX_AIM 0x200class NetClient // 监视客户端每一个操作
{typedef bool (NetClient::* DATAPROC)(char*&, unsigned&);
public:AIM Player; // 玩家角色/* 怪物列表,最好排序,使用lId排序,然后使用二分查找实现快速查询结构用链表或者数组都可以怪物或附近玩家这种东西不可能会很多如果太多电脑处理不过来,所以数组设置为0x100大小差不多够用*/PAIM Aimlst[MAX_AIM]{};
protected:POBJ_DESC ObjectTable = nullptr;// 游戏的数据类型表char* ObjectTxt = nullptr;DATAPROC SendProc[0x100];DATAPROC RecvProc[0x100];bool DefaultProc(char*&, unsigned&);
protected: // 消息处理函数-SENDbool OnClientlogin(char*& buff, unsigned& len); // 登录数据包的处理 I_LOGINbool OnClientStartCreateRole(char*& buff, unsigned& len);  // 申请进入创建角色界面 I_CREATEROLE_STARTbool OnClientDelRole(char*& buff, unsigned& len);bool OnClientSendCustom(char*& buff, unsigned& len);bool OnClientCreateRole(char*& buff, unsigned& len);bool OnClientSelectRole(char*& buff, unsigned& len);
protected: // 消息处理函数-RECVbool OnSvrTips(char*& buff, unsigned& len);bool OnSvrloginOk(char*& buff, unsigned& len);bool OnSvrStartCreateRole(char*& buff, unsigned& len);bool OnSverNotice(char*& buff, unsigned& len);bool OnSverObject(char*& buff, unsigned& len);bool OnSverStruct(char*& buff, unsigned& len);bool OnSvrObjectInit(char*& buff, unsigned& len);bool OnSvrObjectInitEx(char*& buff, unsigned& len);
private:GameWinSock* WinSock;PROLEDATA roles;unsigned rolecount;bool logined = false;bool DelRole(const wchar_t* rolename, unsigned _len);PAIM GetAimById(long long lId);PAIM CreateAim();
public:void virtual Init(GameWinSock * _winSock);/*模拟登陆的方法Id是账号Pass是密码它要基于发送的方法实现,因为我们没有连接socket的操作*/bool login(const char* Id, const char* Pass);bool DelRole(const wchar_t* rolename);bool StartCreateRole();// 用于创建角色bool SelectCamp(const char* _campname);// 选择阵营bool CreateRole(wchar_t* name,double sex, double camp, double face, double occu, const char* photo, const char*infos, const char* txt, double faceShape);// 角色创建// 选择角色并且登录进游戏bool SelectRole(const wchar_t* rolename);// 根据角色名字获取一个登录成功数据包(选择角色列表里的一个数据)PROLEDATA GetRoleByName(const wchar_t* rolename);bool Talk(wchar_t* txt, int PdId = 1, double un = 0.0);bool TalkTo(wchar_t* name, wchar_t* txt, double un = 0.0);bool HeartBeep();// 心跳数据包(5秒)bool HeartLoop();// 延迟心跳数据包(20秒)bool Backtoroles(); // 返回到选择角色界面
public:// 用于拦截游戏删除角色功能bool virtual OnDelRole(wchar_t* rolename, unsigned _len);// 用于拦截游戏登录功能void virtual Onlogin(const char* Id, const char*Pass);// 用于拦截游戏创建角色功能bool virtual OnStartCreateRole(int code);// 拦截创建角色bool virtual OnCreateRole(PNS_CREATEROLE _header, PCREATE_ROLE_DATAS _body);// opcode意思是操作码,count意思是数量,buffStart意思是解码的内容开始,buffend意思是解码的内容结束,buffer是原始的数据,len是原始数据的长度// char& buffer, int& len这俩参数带&的原因是,在 OnSendCustom 里进行修改之后,通过&的方式传递回去bool virtual OnSendCustom(PNET_SEND_CHEAD _coder, char*& buffer, unsigned& len);bool virtual OnSelectRole(wchar_t* rolename);
public:bool virtual OnChooseCamp(PNS_CHOOSECAMP _coder);bool virtual OnChat(PCHAT_DATA _coder);bool virtual OnChatPublic(PCHAT_PUB _coder);bool virtual OnChatPrivate(PCHAT_PRV _coder);bool virtual OnHeartBeep(PHEART_BEEP _coder);bool virtual OnHeartLoop(PHEART_LOOP _coder);// 针对Notice的单独处理bool virtual OnSvrChat(PCHAT_PRV _coder);public:// 处理失败,参数是错误码bool virtual Tips(int code);void virtual loginok(ROLE_DATA* _roles, int count);bool virtual OnScrStartCreateRole(short code,wchar_t* _txt);bool virtual OnSvrNotice(PNET_SEND_CHEAD _coder, int count, char*& buffer, unsigned& len);
public:bool virtual OnRecvData(char*& buff, unsigned& len);bool virtual OnSendData(char*& buff, unsigned& len);bool virtual OnConnect(char*& ip, unsigned& port);};

NetClass.h文件的修改:新加 S_OBJECT_INITEX宏、NR_OBNJECT_INITEX结构体

#pragma once
#include "EnCode.h"// 数据包头
// 发送
#define I_LOGIN 0x2 // 登录
#define I_CREATEROLE_START 0x3 // 创建角色发送数据包的头
#define I_SELECT_ROLE 0x04 // 选择角色进入游戏的数据包头
#define I_CREATEROLE 0x05 // 创建角色给服务端发送的数据包头
#define I_DELROLE 0x6 // 删除角色发送数据包的头
#define I_SEND_CUSTOM 0x0A // 进入游戏之后大部分数据包的头// 接收
#define S_TIPS 0x3 // 服务端返回错误的数据包头
#define S_LOGINOK 0x4 // 登录成功的返回数据包的头
// 创建角色服务端反馈的数据(发送03 01 00 00 00这个数据包服务端返回的数据包头)
#define S_CREATEROLE_START 0x05
#define S_OBJECT 0x9
#define S_STRUCT 0xA
#define S_NOTICE 0x1E // 里面有聊天数据
#define S_NOTICE_COM 0x27 // 里面有聊天数据
#define S_OBJECT_INIT 0x28 // 里面有角色基础信息
#define S_OBJECT_INITEX 0x30 // 里面有附近NPC、玩家初始化信息
/*最多的数据项, 0A开头的数据包里有数据参数个数,这个宏就是用来设定数据参数个数最大数量是多少,0A开头的数据包目前见过最大数据参数个数是十几个,这里写20应该完全够用,如果不够用到时候再增加
*/
#define MAX_SEND_COUNT 100
#define MAX_RECV_COUNT 100
// 发送数据操作码定义
#define SC_CHOOSECAMP 591 // 阵营选择操作码
#define SC_CHAT 1
#define SC_BEEP 81
#define SC_LOOP 1015
// 游戏客户端处理完进入游戏按钮触发的数据包之后,发送的请求
#define SC_INITED 964
#define SC_REONLINE 860
#define SC_INIT_START 661
#define SC_HAND 962
#define SC_HAND_IN 102/*角色怪物初始化结构00 00 00 00 00 30 0B 0082 0B 00 01 82 0B 00 0069 59 F8 C367 49 FF 41FE 55 9C 42C6 70 BA 40A8 8F 07 C406 52 58 413B D0 2A 43C6 70 BA 4000 00 20 4100 00 00 0000 00 00 0000 00 00 0004 00 00 0011 00
*/typedef struct NR_OBNJECT_INITEX {char un1[4];char len = sizeof(NR_OBNJECT_INITEX) - 7;char op = 0x30;short obj_count;long long lId;float x;float h;float y;float face;float tx;float th;float ty;float tface;float un2[5]{};short icount;short type;
}*PNR_OBJINITEX;// 角色初始化数据
/*28  opE6 44 00 01 39 0A 63 04 lId6F 3A 07 C4 x49 C8 81 41 h46 1E 40 44 yBC 74 03 40 face6F 3A 07 C4 tx49 C8 81 41 th46 1E 40 44 tyBC 74 03 40 tface00 00 00 00 un[0]00 00 00 00 un[1]00 00 00 00 un[2]00 00 00 00 un[3]00 00 00 00 un[4]5E 00 icount多出来的type是为了内存对齐才写的
*/
typedef struct NR_OBNJECT_INIT {char un1[6];char len = sizeof(NR_OBNJECT_INIT) - 9;char op = 0x28;long long lId;float x;float h;float y;float face;float tx;float th;float ty;float tface;float un2[5]{};short icount;short type;
}*PNR_OBJINIT;// 保存对象数据描述信息的结构体(游戏数据类型表)
typedef struct OBJECT_DESC {char* name;int type;
}*POBJ_DESC;// 数据头结构体 数据结构约定
// 也就是接收到1E包的头部
typedef struct NR_NOTICE_HEAD {char un;char op;short count;
}NR_HEAD, *PNR_HEAD, *PNR_NOTICE_HEAD;// 选择角色头部
typedef struct NET_SEND_SELECTROLE {char op = 0x04;char buff[83]{};
}NS_SELECTROLE, * PNS_SELECTROLE;// 创建角色数据头部
typedef struct NET_SEHD_CREATEROLE_HEAD {/*char un[0x2];char len = sizeof(NET_SEHD_CREATEROLE_HEAD) - 0x03;这俩代码是为了解决内存对齐的问题,创建角色给服务端发送的05开头的数据包,把05变成00 00 00 05*/char un[0x2];char len = sizeof(NET_SEHD_CREATEROLE_HEAD) - 0x03;char op = 0x05;int index = 0x0; // 选择角色列表索引/*宽字节是2字节0x10是16两个是32,正好满足给服务端发送的创建角色数据包,也就是05开头的数据包*/wchar_t name[0x20];short unst = 0x00;short count = 0x08;
}*PNS_CREATEROLE;// 基类
typedef class NET_SEND_BASE {
public:// 实现编码操作,模拟游戏数据包给服务端发送数据,这个模拟的数据包通过调用 CodeMe函数得到unsigned CodeMe(int size, char* buffer);
}NSR_BASE, *PNET_SEND_BASE, * PNSR_BASE;typedef struct NS_CREATEROLE_HEAD_BUFF :public NET_SEHD_CREATEROLE_HEAD {char buff[0x300]{};
}*PNS_CREATEROLEBUFF;// 解析约定结构体
// 创建角色数据结构体
typedef class CREATE_ROLE_DATAS:public NET_SEND_BASE {
public:GDOUBLE sex;//1.000000 性别 0 男 1 女GDOUBLE camp;//1.000000 阵营 1 艾森赫特 2 格兰蒂尔GDOUBLE face;//1.000000 种族 1 布冯特人 3 格洛玛人 4 尤恩图人 6 喀什人GDOUBLE occu;//3.000000 职业 1 仲裁者 3秘法师 6 猎魔人 8 元素法师GCHAR Photo;//gui\BG_team\TeamRole\Teamrole_zq_humF_001.PNGGCHAR Infos;//Face,0;Hat,0;Eyes,0;Beard,0;Ears,0;Tail,0;Finger,0;Cloth,0;Pants,0;Gloves,0;Shoes,0;Trait,0;HairColor,0;SkinColor,0;SkinMtl,0;Tattoo,0;TattooColor,16777215;GCHAR Txt;// 细节编辑捏脸GDOUBLE faceSahpe;//0.000000 脸型 0 1 2 3
}* PCREATE_ROLE_DATAS;// 发送数据标准头 0xA数据包
typedef struct NET_SEND_HEAD {// 为了内存对齐,当前结构不可能超过255,所以这样写是没问题的char len = sizeof(NET_SEND_HEAD) - 1;char op = 0x0A;short unst = 0;int unnt[4]{};short unst1 = 0;short count = 0;
}*PNET_SEND_HEAD;
// 开始创建角色的数据结构
// 申请创建角色结构体
typedef struct NET_CREATEROLE_START {char un[0x02];// 用来做内存对齐char len = sizeof(NET_CREATEROLE_START) - 3;// 用来做内存对齐char op = 0x03;int code = 0x01;
}*PNET_CREATEROLE_START;// 删除角色数据结构
typedef struct DATA_DELROLE {char op;char buff[0x1F];int len;int un[8] = { 0x01 , 0x03 };}*PDATADELROLE;/*数据包还原结构体要注意内存对齐,如果数据不满4字节,它字段会补齐比如结构体里有一个char变量,它是1字节,在内存里它可能会为了内存对齐让它变成4字节,所以这要注意
*/
// 登录数据
typedef struct DATA_LOGIN {int op = 0x0300;char buff[0x10]{};int lenId = 0x10;/*这个是登录的账号,它可能会变成0x20或更长,现在默认让它0x10读的时候以长度为准就好了*/char Id[0x10]{};int lenPass = 0x10;/*这个是登录的密码,它可能会变成0x20或更长,现在默认让它0x10读的时候以长度为准就好了*/char Pass[0x10]{};int lenCode = 0x10;char Code[0x10]{};int eop = 0x01;
}*PDATALOGIN;
typedef struct DATA_LOGIN_OK {// 登录成功数据包头int un[8] = { 0, 0, 0x76B, 0x0C, 0x1E,0, 0, 0 };int index = 0;int RoleCount = 0;
}*PDATALOGINOK;// 发送数据包含内存空间,也就是数据参数,数据参数的意思看44文章里的内容
typedef struct NET_SEND_BUFF:public NET_SEND_HEAD {char buff[0x1000]{};
}*PNET_SEND_BUFF;typedef class  NET_SEND_CHEAD:public NET_SEND_BASE {
public:union {GCHAR msgHeader;GINT opcode;};
public:NET_SEND_CHEAD() {};~NET_SEND_CHEAD() {};
}NSR_CHEAD, *PNSR_CHEAD, *PNET_SEND_CHEAD;// 选择角色
typedef class NET_SEND_CHOOSECAMP :public NET_SEND_CHEAD {
public:GCHAR camps;
}*PNS_CHOOSECAMP;// 心跳包数据结构
typedef class HEART_BEEP :public NET_SEND_CHEAD {
public:// 20秒一次GINT tick;//3
}*PHEART_BEEP;typedef class HEART_LOOP :public NET_SEND_CHEAD {
public:// 5秒一次GINT64 tick;//9320695GCHAR txt;//}*PHEART_LOOP;typedef struct ONLINE_HAND: public NSR_CHEAD{GINT step; // 2GINT state; // 1
}*PONLINE_HAND;typedef struct ONLINE_INIT : public NSR_CHEAD {GINT state;//0GDOUBLE un;//180000000.000000
}*PONLINE_INIT;
// 登陆后角色数据区
// 聊天数据包的数据结构
typedef class CHAT_DATA :public NET_SEND_CHEAD {
public:GINT ChartId;//1
}*PCHAT_DATA;
typedef class CHAT_PUBLIC :public CHAT_DATA {
public:GUTF16 txt;//[222222222222222222222222222222222222222222<img src="Face37"  valign="bottom"  only="line"/>]GDOUBLE un;//0.000000
}*PCHAT_PUB;
typedef class CHAT_PRIVATE :public CHAT_DATA {
public:GUTF16 txt;//[222222222222222222222222222222222222222222<img src="Face37"  valign="bottom"  only="line"/>]GUTF16 name;//[皮革兔]GDOUBLE un;//0.000000
}*PCHAT_PRV;// 登陆后角色数据包
typedef class ROLE_DATA {// 登录成功数据包
public:GBYTE byte;GINT index;GINT un1;GUTF16 name;GUTF16 infos;GINT un2;GINT64 un3;
}*PROLEDATA;

这篇关于71.网络游戏逆向分析与漏洞攻防-角色与怪物信息的更新-分析并利用角色与怪物创建的数据包的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/955122

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

AI行业应用(不定期更新)

ChatPDF 可以让你上传一个 PDF 文件,然后针对这个 PDF 进行小结和提问。你可以把各种各样你要研究的分析报告交给它,快速获取到想要知道的信息。https://www.chatpdf.com/

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多