Доброго времени стуок, были проведены следующие операции:
1)Был снят дамп клиента(game.dll) с помощью x64dbg
2)Обошли Themida с помощью плагина Scylla
3)И этот дамп был проанализирован в IDA
Собственно получил вот это:
(в данном конкретном случае рассмотрим пакет SM_BROKER_SERVICE)
Objective-C:
__int64 __fastcall sub_59CEB320(__int64 a1, __int64 a2)
{
signed __int64 v2; // r8
__int64 result; // rax
char v4; // cl
int v5; // er11
char v6; // dl
__int64 v7; // r10
__int64 v8; // r9
__int64 v9; // r8
v2 = *(_QWORD *)(a2 + 24) + *(signed int *)(a2 + 32) + 1i64;
result = *(unsigned __int8 *)(*(_QWORD *)(a2 + 24) + *(signed int *)(a2 + 32));
switch ( (_DWORD)result )
{
case 0:
result = sub_59CEB440(a1, (int *)v2);
break;
case 1:
result = sub_59CEB780(a1, v2);
break;
case 2:
result = sub_5965BA80(
a1,
(unsigned int)*(char *)(*(_QWORD *)(a2 + 24) + *(signed int *)(a2 + 32) + 1i64),
*(unsigned int *)(*(_QWORD *)(a2 + 24) + *(signed int *)(a2 + 32) + 2i64),
*(_QWORD *)(*(_QWORD *)(a2 + 24) + *(signed int *)(a2 + 32) + 6i64));
break;
case 3:
result = sub_59CEBAC0(a1, v2);
break;
case 4:
result = sub_59CEBD50(a1, v2);
break;
case 5:
result = sub_59CEBEF0(a1, v2);
break;
case 6:
if ( !*(_BYTE *)v2 )
result = sub_59B761E0(a1);
break;
case 7:
v4 = *(_BYTE *)v2;
v5 = *(_DWORD *)(*(_QWORD *)(a2 + 24) + *(signed int *)(a2 + 32) + 2i64);
v6 = *(_BYTE *)(*(_QWORD *)(a2 + 24) + *(signed int *)(a2 + 32) + 14i64);
v7 = *(_QWORD *)(v2 + 22);
v8 = *(_QWORD *)(v2 + 14);
v9 = *(_QWORD *)(v2 + 5);
result = sub_59AECD00(1145446400i64);
break;
default:
return result;
}
return result;
}
(рассмотрим 0 кейс)
C-подобный:
__int64 __fastcall sub_59CEB440(__int64 a1, int *a2)
{
__int16 v2; // bx
int v3; // edi
char v4; // r14
__int16 v5; // r15
char *v6; // rsi
__int64 v7; // r14
__int128 v8; // xmm0
__int128 v9; // xmm1
int v10; // er15
unsigned int v11; // er12
__int64 v12; // r9
__int64 v13; // r13
__int128 v14; // xmm0
__int128 v15; // xmm1
__int128 v16; // xmm0
__int128 v17; // xmm1
__int128 v18; // xmm0
__int128 v19; // xmm1
__int64 v20; // rax
signed int v21; // er8
__int128 v22; // xmm0
__int128 v23; // xmm1
__int128 v24; // xmm0
signed __int64 v25; // rcx
__int16 v26; // dx
__int16 *i; // rax
signed __int64 v28; // rcx
__int16 *v29; // rdx
__int16 v30; // ax
signed int j; // er8
char v32; // r8
char v33; // di
__int16 v34; // bx
int v35; // er11
__int16 v36; // r10
__int64 v37; // rax
__int16 v39; // [rsp+70h] [rbp-88h]
__int64 v40; // [rsp+72h] [rbp-86h]
__int64 v41; // [rsp+7Ah] [rbp-7Eh]
__int64 v42; // [rsp+82h] [rbp-76h]
__int64 v43; // [rsp+8Ah] [rbp-6Eh]
__int64 v44; // [rsp+92h] [rbp-66h]
__int16 v45; // [rsp+9Ah] [rbp-5Eh]
__int16 v46; // [rsp+A0h] [rbp-58h]
__int64 v47; // [rsp+A2h] [rbp-56h]
__int64 v48; // [rsp+AAh] [rbp-4Eh]
__int64 v49; // [rsp+B2h] [rbp-46h]
__int64 v50; // [rsp+BAh] [rbp-3Eh]
__int64 v51; // [rsp+C2h] [rbp-36h]
__int64 v52; // [rsp+CAh] [rbp-2Eh]
__int16 v53; // [rsp+D2h] [rbp-26h]
__int128 v54; // [rsp+D8h] [rbp-20h]
__int128 v55; // [rsp+E8h] [rbp-10h]
__int128 v56; // [rsp+F8h] [rbp+0h]
__int128 v57; // [rsp+108h] [rbp+10h]
__int128 v58; // [rsp+118h] [rbp+20h]
__int128 v59; // [rsp+128h] [rbp+30h]
__int128 v60; // [rsp+138h] [rbp+40h]
__int128 v61; // [rsp+148h] [rbp+50h]
__int128 v62; // [rsp+158h] [rbp+60h]
__int128 v63; // [rsp+168h] [rbp+70h]
__int128 v64; // [rsp+178h] [rbp+80h]
__int64 v65; // [rsp+188h] [rbp+90h]
__int16 v66; // [rsp+190h] [rbp+98h]
char v67; // [rsp+192h] [rbp+9Ah]
__int128 v68; // [rsp+198h] [rbp+A0h]
__int128 v69; // [rsp+1A8h] [rbp+B0h]
__int128 v70; // [rsp+1B8h] [rbp+C0h]
__int128 v71; // [rsp+1C8h] [rbp+D0h]
__int128 v72; // [rsp+1D8h] [rbp+E0h]
__int128 v73; // [rsp+1E8h] [rbp+F0h]
__int128 v74; // [rsp+1F8h] [rbp+100h]
__int128 v75; // [rsp+208h] [rbp+110h]
__int128 v76; // [rsp+218h] [rbp+120h]
__int128 v77; // [rsp+228h] [rbp+130h]
__int128 v78; // [rsp+238h] [rbp+140h]
__int64 v79; // [rsp+248h] [rbp+150h]
__int16 v80; // [rsp+250h] [rbp+158h]
char v81; // [rsp+252h] [rbp+15Ah]
v2 = *(_WORD *)((char *)a2 + 7);
v3 = *a2;
v4 = *((_BYTE *)a2 + 4);
v5 = *(_WORD *)((char *)a2 + 5);
v6 = (char *)a2 + 9;
sub_59C45F30(a1);
sub_59AE7AE0(1145446400i64, v5, v3, v4);
if ( v2 > 0 )
{
v7 = (unsigned int)v2;
do
{
v8 = *((_OWORD *)v6 + 2);
v9 = *((_OWORD *)v6 + 3);
v10 = *(_DWORD *)v6;
v11 = *((_DWORD *)v6 + 1);
v12 = *((_QWORD *)v6 + 1);
v13 = *((_QWORD *)v6 + 3);
v40 = 0i64;
v41 = 0i64;
v54 = v8;
v14 = *((_OWORD *)v6 + 4);
v55 = v9;
v41 = 0i64;
v42 = 0i64;
v43 = 0i64;
v15 = *((_OWORD *)v6 + 5);
v56 = v14;
v16 = *((_OWORD *)v6 + 6);
v44 = 0i64;
v45 = 0;
v47 = 0i64;
v48 = 0i64;
v49 = 0i64;
v50 = 0i64;
v57 = v15;
v17 = *((_OWORD *)v6 + 7);
v58 = v16;
v51 = 0i64;
v52 = 0i64;
v53 = 0;
v18 = *((_OWORD *)v6 + 8);
v59 = v17;
v19 = *((_OWORD *)v6 + 9);
v20 = *((_QWORD *)v6 + 26);
v39 = 0;
v46 = 0;
v21 = 26;
v60 = v18;
v22 = *((_OWORD *)v6 + 10);
v61 = v19;
v23 = *((_OWORD *)v6 + 11);
v62 = v22;
v24 = *((_OWORD *)v6 + 12);
v63 = v23;
v64 = v24;
v65 = v20;
v66 = *((_WORD *)v6 + 108);
v25 = (signed __int64)(v6 + 219);
v67 = v6[218];
v26 = *(_WORD *)(v6 + 219);
for ( i = &v46; v26; v25 += 2i64 )
{
if ( --v21 > 0 )
{
*i = v26;
++i;
}
v26 = *(_WORD *)(v25 + 2);
}
v28 = v25 + 2;
*i = 0;
v29 = &v39;
v30 = *(_WORD *)v28;
for ( j = 26; v30; v28 += 2i64 )
{
if ( --j > 0 )
{
*v29 = v30;
++v29;
}
v30 = *(_WORD *)(v28 + 2);
}
v32 = *(_BYTE *)(v28 + 10);
v33 = *(_BYTE *)(v28 + 2);
v34 = *(_WORD *)(v28 + 3);
v35 = *(_DWORD *)(v28 + 5);
*v29 = 0;
v36 = *(unsigned __int8 *)(v28 + 9);
v68 = v54;
v69 = v55;
v6 = (char *)(v28 + 11);
v70 = v56;
v71 = v57;
v72 = v58;
v73 = v59;
v74 = v60;
v75 = v61;
v76 = v62;
v77 = v63;
v78 = v64;
v79 = v65;
v80 = v66;
v81 = v67;
v37 = sub_59C45BA0(
(__int64)&v62,
v10,
v11,
v12,
v13,
(unsigned __int64)&v68,
(unsigned __int64)&v39,
(unsigned __int64)&v46,
v33,
v34,
v35,
v36,
v32 != 0);
if ( v37 )
sub_59AE7C40(1145446400i64, v37);
--v7;
}
while ( v7 );
}
return sub_59AE8060(1145446400i64);
}
Посмотреть вложение 96795
Посмотреть вложение 96796
Посмотреть вложение 96797
структура пакета на эмуляторе:
(0 кейс)
Java:
package com.aionemu.gameserver.network.aion.serverpackets;
import java.sql.Timestamp;
import java.util.Calendar;
import com.aionemu.gameserver.model.gameobjects.BrokerItem;
import com.aionemu.gameserver.model.gameobjects.Item;
import com.aionemu.gameserver.network.aion.AionConnection;
import com.aionemu.gameserver.network.aion.AionServerPacket;
import com.aionemu.gameserver.network.aion.iteminfo.ItemInfoBlob;
import com.aionemu.gameserver.network.aion.iteminfo.ItemInfoBlob.ItemBlobType;
public class SM_BROKER_SERVICE extends AionServerPacket {
private enum BrokerPacketType {
SEARCHED_ITEMS(0), REGISTERED_ITEMS(1), REGISTER_ITEM(3), SHOW_SETTLED_ICON(5), SETTLED_ITEMS(5),
REMOVE_SETTLED_ICON(6), AVE_LOW_HIGH_ITEM(7);
private int id;
private BrokerPacketType(int id) {
this.id = id;
}
private int getId() {
return id;
}
}
private BrokerPacketType type;
private BrokerItem[] brokerItems;
private int itemsCount;
private int startPage;
private int message;
private long settled_kinah;
private int itemUniqueId;
private long Ave7day;
private long CurrentLow;
private long CurrentHigh;
private boolean IsLowHighSame;
public SM_BROKER_SERVICE(BrokerItem brokerItem, int message, int itemsCount) {
this.type = BrokerPacketType.REGISTER_ITEM;
this.brokerItems = new BrokerItem[] { brokerItem };
this.message = message;
this.itemsCount = itemsCount;
}
public SM_BROKER_SERVICE(int message) {
this.type = BrokerPacketType.REGISTER_ITEM;
this.message = message;
}
public SM_BROKER_SERVICE(BrokerItem[] brokerItems) {
this.type = BrokerPacketType.REGISTERED_ITEMS;
this.brokerItems = brokerItems;
}
public SM_BROKER_SERVICE(BrokerItem[] brokerItems, long settled_kinah) {
this.type = BrokerPacketType.SETTLED_ITEMS;
this.brokerItems = brokerItems;
this.settled_kinah = settled_kinah;
}
public SM_BROKER_SERVICE(BrokerItem[] brokerItems, int itemsCount, int startPage) {
this.type = BrokerPacketType.SEARCHED_ITEMS;
this.brokerItems = brokerItems;
this.itemsCount = itemsCount;
this.startPage = startPage;
}
public SM_BROKER_SERVICE(boolean showSettledIcon, long settled_kinah) {
this.type = showSettledIcon ? BrokerPacketType.SHOW_SETTLED_ICON : BrokerPacketType.REMOVE_SETTLED_ICON;
this.settled_kinah = settled_kinah;
}
public SM_BROKER_SERVICE(int itemUniqueId, long Ave7day, long CurrentLow, long CurrentHigh, boolean IsLowHighSame) {
this.type = BrokerPacketType.AVE_LOW_HIGH_ITEM;
this.itemUniqueId = itemUniqueId;
this.Ave7day = Ave7day;
this.CurrentLow = CurrentLow;
this.CurrentHigh = CurrentHigh;
this.IsLowHighSame = IsLowHighSame;
}
@Override
protected void writeImpl(AionConnection con) {
switch (type) {
case SEARCHED_ITEMS:
writeSearchedItems();
break;
case REGISTERED_ITEMS:
writeRegisteredItems();
break;
case REGISTER_ITEM:
writeRegisterItem();
break;
case SHOW_SETTLED_ICON:
writeShowSettledIcon();
break;
case REMOVE_SETTLED_ICON:
writeRemoveSettledIcon();
break;
case SETTLED_ITEMS:
writeShowSettledItems();
break;
case AVE_LOW_HIGH_ITEM:
writeItemAveLowHigh();
break;
}
}
private void writeItemAveLowHigh() {
writeC(type.getId());
writeC(0x00);
writeD(itemUniqueId);
writeQ(Ave7day);
writeC(IsLowHighSame ? 0x02 : 0x00);
writeQ(CurrentLow);
writeQ(CurrentHigh);
}
private void writeSearchedItems() {
writeC(type.getId());
writeD(itemsCount);
writeC(0);
writeH(startPage);
writeH(brokerItems.length);
for (BrokerItem item : brokerItems) {
writeItemInfo(item);
}
}
private void writeRegisteredItems() {
writeC(type.getId());
writeD(0x00);
writeH(brokerItems.length);
for (BrokerItem brokerItem : brokerItems) {
writeRegisteredItemInfo(brokerItem);
}
}
private void writeRegisterItem() {
writeC(type.getId());
writeC(message);
if (message == 0) {
writeC(itemsCount + 1);
BrokerItem itemForRegistration = brokerItems[0];
writeRegisteredItemInfo(itemForRegistration);
} else {
writeB(new byte[107]);
}
}
private void writeShowSettledIcon() {
writeC(type.getId());
writeQ(settled_kinah);
writeD(0x00);
writeH(0x00);
writeH(0x01);
writeC(0x00);
}
private void writeRemoveSettledIcon() {
writeH(type.getId());
}
private void writeShowSettledItems() {
writeC(type.getId());
writeQ(settled_kinah);
writeH(brokerItems.length);
writeD(0x00);
writeC(0x00);
writeH(brokerItems.length);
for (BrokerItem settledItem : brokerItems) {
writeD(settledItem.getItemId());
if (settledItem.isSold()) {
writeQ(settledItem.getPrice());
} else {
writeQ(0);
}
writeQ(settledItem.getItemCount());
writeQ(settledItem.getItemCount());
writeD((int) ((settledItem.getSettleTime().getTime() / 60000) & 0xffffffffl));
Item item = settledItem.getItem();
if (item != null) {
ItemInfoBlob.newBlobEntry(ItemBlobType.MANA_SOCKETS, null, item).writeThisBlob(getBuf());
} else {
writeB(new byte[187]);
}
writeS(settledItem.getItemCreator());
writeB(new byte[8]);// 5.5
}
}
private void writeRegisteredItemInfo(BrokerItem brokerItem) {
Item item = brokerItem.getItem();
writeD(brokerItem.getItemUniqueId());
writeD(brokerItem.getItemId());
writeQ(brokerItem.getPrice());
writeQ(item.getItemCount());
writeQ(item.getItemCount());
Timestamp currentTime = new Timestamp(Calendar.getInstance().getTimeInMillis());
int daysLeft = (int) ((brokerItem.getExpireTime().getTime() - currentTime.getTime()) / 86400000);
writeC(daysLeft);
ItemInfoBlob.newBlobEntry(ItemBlobType.MANA_SOCKETS, null, item).writeThisBlob(getBuf());
writeS(brokerItem.getItemCreator());
ItemInfoBlob.newBlobEntry(ItemBlobType.PREMIUM_OPTION, null, item).writeThisBlob(getBuf());
ItemInfoBlob.newBlobEntry(ItemBlobType.IDIAN_INFO, null, item).writeThisBlob(getBuf());
//ItemInfoBlob.newBlobEntry(ItemBlobType.WRAPP_INFO, null, item).writeThisBlob(getBuf());
writeC(0x00);
writeC(brokerItem.isSplitSell() ? 0x01 : 0x00);
int commission = brokerItem.getSellCommission();
writeD(commission); // unk 5.5
writeD(0x00); // unk 5.5
}
private void writeItemInfo(BrokerItem brokerItem) {
Item item = brokerItem.getItem();
writeD(item.getObjectId());
writeD(item.getItemTemplate().getTemplateId());
writeQ(brokerItem.getPrice());
writeQ(brokerItem.getPrice());
writeQ(item.getItemCount());
ItemInfoBlob.newBlobEntry(ItemBlobType.MANA_SOCKETS, null, item).writeThisBlob(getBuf());
writeS(brokerItem.getSeller());
writeS(brokerItem.getItemCreator());
ItemInfoBlob.newBlobEntry(ItemBlobType.PREMIUM_OPTION, null, item).writeThisBlob(getBuf());
ItemInfoBlob.newBlobEntry(ItemBlobType.IDIAN_INFO, null, item).writeThisBlob(getBuf());
//ItemInfoBlob.newBlobEntry(ItemBlobType.WRAPP_INFO, null, item).writeThisBlob(getBuf());
writeC(0x00);
writeC(brokerItem.isSplitSell() ? 0x01 : 0x00);
}
}
казалось бы структура похожа на то что ожидает клиент, но есть некоторые проблемы с поиском предметов(в некоторых вкладках) в брокере и не показывает иконку упаковки на предмете в окне поиска, казалось бы что проблема в структуре
ItemInfoBlob.newBlobEntry(ItemBlobType.MANA_SOCKETS, null, item).writeThisBlob(getBuf());
но последние поля как имя продавца и сплитселл читаются адекватно,а при попытках поменять струкутуру блоба натыкаешься на вылет игры либо некорректное отображение предметов в брокере
другие серверы в качестве доноров не смогли помочь (идентичной и более ранних версий), как и снифф актуальной(рабочей) версии эмулятора
сравнение сниффов было буквально проверено по байтам с нашей структурой
собственно разницы там не было
Вопросы:
Где ошибка? В стратегии починки пакета( не тот инструментарий либо иду не достаточно глубоко )?
Насколько Scylla вообще способна обходить защиту которую имеет клиент Aion?
Допустим я знаю размер и примерную структуру пакета у меня вообще есть возможно узнать какие данные ожидает клиент?
Если есть некоторые рекомендации по извлечению информации из клиента касательно структуры пакетов, то я буду очень благодарен.