Jump to content
Korean Random

Monstrofil

User
  • Content Count

    97
  • Joined

  • Last visited

  • Days Won

    7

Everything posted by Monstrofil

  1. Так, пока разбирался с FRAGMENT пакетами ещё одна загадка нарисовалась. На клиент приходит вызов метода с двумя BLOB и INT8 следующего содержания # onGetMapList # arg1 (blob len=14186, 6a37 in hex) arg2 (blob len=13, 0d in hex) arg3 (int8) # 789c959d057...c51695c2bf7de0ffd0b5f8ce 789c6b600a65d0030003ba0106 0 Размер первого Blob = 14186 + второго 13 + int8 дает 14200 байт. Но если посмотреть в пакеты, то там следующие значения: # in packet # methodid size of arg1 (14205) arg1 (blob len=14186, 14205-14186 = 19) size of arg2 arg2 arg3 (int) # 7c ff7d370000 789c959d057...c51695c2bf7de0ffd0b5f8ce 0d 789c6b600a65d0030003ba0106 00 Размер второго Blob описан правильно, Int8 присутствует, но размер парвого blob декодируется как 14205, что больше чем размер payload в целом. Размер первого blob распаковываю используя магию от разработчиков BW по упаковыванию int32 в три байта. https://github.com/Monstrofil/bigworld-2.0/blob/5969290b3f1710910c7cecdad6a34b2016fad9e7/lib/cstdmf/binary_stream.hpp#L265 https://github.com/Monstrofil/bigworld-2.0/blob/5969290b3f1710910c7cecdad6a34b2016fad9e7/lib/cstdmf/binary_stream.ipp#L162 Кто-нибудь уже сталкивался с подобным? upd: целый день с этим провозился и только сейчас заметил что размер blob по какой-то причине дописан в конец payload, сразу после uint8. ff6a3700 А ff7d370000 это размер всего payload в целом UPD 09.11. Разбирался сегодня с выравниванием под blowfish. Предварительно пришел к следующему: последний байт = количество добавленных байт - 1. Добавленные байты находятся сразу перед efbeadde и, похоже, ничего не значат. По какой-то причине добавляются не нулевые значения, причин этого понять не удалось. # >>> BigWorld.player().base.getAccountData('', '', 0, 0, 'enddata123', 42) # 5804 01d3abf17a ba19 00 00 00000000 00000000 000a656e6464617461313233 2a000000 4f150000 c8190000 efbeadde 01 # >>> BigWorld.player().base.getAccountData('', '', 0, 0, 'enddata12', 42) # 5804 01d3abf17a ba18 00 00 00000000 00000000 0009656e64646174613132 2a000000 a3160000 521b0000 00 efbeadde 02 # >>> BigWorld.player().base.getAccountData('', '', 0, 0, 'enddata1', 42) # 5804 01d3abf17a ba17 00 00 00000000 00000000 0008656e646461746131 2a000000 3a150000 b1190000 5f9f efbeadde 03 # >>> BigWorld.player().base.getAccountData('', '', 0, 0, 'enddata', 42) # 5804 01d3abf17a ba16 00 00 00000000 00000000 0007656e6464617461 2a000000 10120000 f4150000 545f52 efbeadde 04 # >>> BigWorld.player().base.getAccountData('', '', 0, 0, 'enddat', 42) # 5804 01d3abf17a ba15 00 00 00000000 00000000 0006656e64646174 2a000000 b8130000 df170000 04545f52 efbeadde 05 # >>> BigWorld.player().base.getAccountData('', '', 0, 0, 'endda', 42) # 5804 01d3abf17a ba14 00 00 00000000 00000000 0005656e646461 2a000000 c5130000 eb170000 3767da15a1 efbeadde 06 # >>> BigWorld.player().base.getAccountData('', '', 0, 0, 'endd', 42) # 5804 01d3abf17a ba13 00 00 00000000 00000000 0004656e6464 2a000000 d1130000 f6170000 000004545f52 efbeadde 07 # >>> BigWorld.player().base.getAccountData('', '', 0, 0, 'end', 42) # 5804 01d3abf17a ba12 00 00 00000000 00000000 0003656e64 2a000000 dd130000 01180000 00000004545f52 efbeadde 08 # >>> BigWorld.player().base.getAccountData('', '', 0, 0, 'en', 42) # 5804 01d3abf17a ba11 00 00 00000000 00000000 0002656e 2a000000 a0140000 f0180000 efbeadde 01 Написал код который заменяет рандомный мусор в дополнительных байтах на нужное количество 0xff, скормил такие ответы клиенту и серверу и ничего не поменялось.
  2. Ну, это может сработать в сторону сервера, но не наоборот. Когда на клиенте 28 энтити и нужно у каждой из них повызывать методы то явно где-то должен быть entityID в запросе. Ладно, проверю эту теорию позже. Тут ещё в теме в самом начале обсуждали флаги пакетов и отметили FLAG_IS_RELIABLE одновременно как 0x1 и 1 << 10, хотя в сорсах BW он вообще 0x0010. Учитывая что FLAG_IS_FRAGMENT = 0x0020 из сорсов совпадает с реальностью, FLAG_IS_RELIABLE на первой странице это опечатка, или действительно поменяли идентификатор?
  3. Такое поведение только при общении с Base? А в бою что будет, где 28+ энтитей создается? Или там уже общение с Cell и чуть другие параметры в пакетах? До Cell ещё не добрался.
  4. checkGamePing и onCheckGamePing это уже методы Account, Login к этому времени уже давно уничтожается. Я просто привожу эти примеры поскольку с Account у меня та же проблема что с Login. Если вдаваться чуть подробнее, то происходит следующее: 1. приходит сообщение entityCreate, в нем есть entityId - уникальный идентификатор entity и наверняка есть request id, на который я в данный момент подзабил. 2. через время уходят - приходят запросы на вызов base/client методов, в которых уже не фигурирует entity id. как клиент определяет какой из существующих на данный момент в клиенте entity нужно вызвать метод onCheckGamePing (предварительно декодировав его из exposedId), если в самом запросе нет entityId? неужели он хранит цепочку request id для определения entityId исходя из первого entitycreate? В реплеях было проще, там все запросы на entitymethod лежат сразу с entityid и exposedinxex =/ def __init__(self, stream): self.entityId, = struct.unpack('I', stream.read(4)) self.messageId, = struct.unpack('I', stream.read(4)) self.payload = BinaryStream(stream)
  5. Хм, я изначально предположил что этот пакет сервер просто присылает с сервера периодически, но, похоже, у него есть зеркальный запрос checkGamePing идущий от клиента к base server. # client -> base (checkGamePing) # flags? counter? packet id? method id? request id? # request -> 5804 01ff 93 15 9a960800c9302a00 00000000 3a000000 77000000 59 efbeadde02 # 5804 01ff 93 15 9a9608005c312a00 00000000 3b000000 78000000 2f efbeadde02 # 5804 01ff 93 15 9a960800df312a00 00000000 3c000000 79000000 a7 efbeadde02 # base -> client (onCheckGamePing) # flags? counter? packet id? method id time request id? # response -> 5804 1369 1a 70 c9302a0000000000 77000000 3b000000 de0695d2 f9 efbeadde06 # 5804 136a 1a 70 5c312a0000000000 78000000 3c000000 08c84f70 8d efbeadde06 # 5804 136b 1a 70 df312a0000000000 79000000 3d000000 7468438d e3 efbeadde06 Если присмотреться, то 77000000 из футера первого сообщения клиента соответствует первому ответу сервера и так далее. А 3b000000 которое вернул сервер в футере используется в следующем запросе от клиента к base. И всё равно в упор не вижу где связь с entity id =/
  6. @Dragon armor привет, если ещё бываешь тут, подскажи пожалуйста пару моментов. Вклинился между клиентом и сервером wows, получаю и дешифрую пакеты используя blowfish, но возникает много вопросов. 1. Получаю пакет entityCreate, в данном случае происходит создание Login entity. Декодирую, получаю entityid и список properties (для этой entity в корабликах они пустые) # login entity create payload (no props on setup) # header? ?? entityid def id ?? footer? # 5804138f0508 00 e9e01330 0600 00 01020000000100000075f05577efbeadde05 login_create = b'X\x04\x13\x8f\x05\x08\x00\xe9\xe0\x130\x06\x00\x00\x01\x02\x00\x00\x00\x01\x00\x00\x00u\xf0Uw\xef\xbe\xad\xde\x05' 2. Получаю другой пакет через некоторое время, с запросом выполнить метод syncUserStorage (угадал по параметрам, перепроверил через python shell, это точно он). # login call sync method # syncUserStorage ([clientDataRevision: <UInt32>, spaID: <UInt64>, urls: <Array> [<String>, ...]]) # exposed id = 2 (with 0x44 offset = 0x46) # header method id ?? clientDataRevision spaID urls.len string.len # 580413941a 46 6b 01000000 5f79581e00000000 01 5d # string = https://s-ed1.cloud.gcore.lu/530-wows-users-params/hhh/ggg/fff/eee/ddd/c1e5/b21e/f509114719d1 # 68747470733a2f2f732d6564312e636c6f75642e67636f72652e6c752f3533302d776f77732d75736572732d706172616d732f6868682f6767672f6666662f6565652f6464642f633165352f623231652f663530393131343731396431 # footer # 090000000100000000efbeadde02 login_sync = b'X\x04\x13\x94\x1aFk\x01\x00\x00\x00_yX\x1e\x00\x00\x00\x00\x01]https://s-ed1.cloud.gcore.lu/530-wows-users-params/hhh/ggg/fff/eee/ddd/c1e5/b21e/f509114719d1\t\x00\x00\x00\x01\x00\x00\x00\x00\xef\xbe\xad\xde\x02' В пакете есть exposed id, есть данные для вызова метода, но я в упор не вижу как определить entity id для которой нужно выполнить указанные действия. Полученный в первом сообщении e9e01330 вообще больше нигде в декодированном трафике не фигурирует. Оба пакета меньше 32 байт, делиться в моем понимании на части они не должны, так как же клиент определяет какой для entity нужно вызвать метод? upd, а вот тот же call method для Account, тоже нет никаких упоминаний про entity id # account id on creation b'\xb5H\x0f0' # 0x44 + 44 onCheckGamePing ([time: <UInt64>]) # method id time footer? # 580413691a 70 f971310000000000 770d0000780c0000f32935c5beefbeadde06 onCheckGamePing = b'X\x04\x13i\x1ap\xf9q1\x00\x00\x00\x00\x00w\r\x00\x00x\x0c\x00\x00\xf3)5\xc5\xbe\xef\xbe\xad\xde\x06' upd2: всли посмотреть в разрезе времени как меняются параметры, то получается такая картина, ничего статического что могло бы служить entity id я не наблюдаю # account id on creation b'\xb5H\x0f0' aka b5480f30 # 44 onCheckGamePing ([time: <UInt64>]) # flags? counter? packet id? method id time footer? # 5804 1369 1a 70 f971310000000000 770d0000 780c0000 f32935c5be efbeadde06 # 5804 136a 1a 70 5c312a0000000000 78000000 3c000000 08c84f708d efbeadde06 # 5804 136d 1a 70 06332a0000000000 7b000000 3f000000 ce0e17c551 efbeadde06 # 5804 136e 1a 70 8d332a0000000000 7c000000 40000000 e1f376a5f2 efbeadde06 # 5804 136f 1a 70 20342a0000000000 7d000000 41000000 d33eb24317 efbeadde06 # 5804 1370 1a 70 b4342a0000000000 7e000000 42000000 1cd046fac3 efbeadde06 # 5804 1371 1a 70 3a352a0000000000 7f000000 43000000 882900003a efbeadde06 # 5804 1372 1a 70 ce352a0000000000 80000000 45000000 db5c1dbb1e efbeadde06 upd3: нашел ещё пакет в котором сразу два вызова entitymethod, тот же запуск игры что и в предыдущих. Почему-то нет информации о том что пакет содержит два payload в первых 8 байтах, видимо искать нужно в footer'e. Возможно там же где-то entity_id, но его я в упор не вижу. # onCheckGamePing + onTotalUsersCountUpdate in one packet # 580413871a7087412a00000000001a802f2e80027d71012855024e414d682055044153494171024db917550d43555252454e545f5245414c4d71034d3362752e950000005a00000022aeaaefbeadde04 # flags? counter? packet id? method id time = 2769287 # 5804 1387 1a 70 87412a0000000000 # # packet id? method id ?? size # 1a 80 2f 2e # # pickle data {'NA': 8423, 'CURRENT_REALM': 25482, 'ASIA': 4917} # 80027d71012855024e414d682055044153494171024db917550d43555252454e545f5245414c4d71034d3362752e # footer # 95000000 5a000000 22aeaa efbeadde04
  7. Делаешь MITM для перехвата трафика между клиентом и сервером и исследуешь, исследуешь, исследуешь.
  8. Если клиент что-то отображает при воспроизведении, то эта инфа в реплее есть. Если не отображает - стоит поискать сначала используя удаленную python-консольку, а потом уже в реплей лезть.
  9. хм, реально нет куска видимо скопировать не успели :)
  10. В коде поискать подсказки не пробовал? Или слишком отличается то что было слито от того что в wot используется? https://github.com/Monstrofil/bigworld-2.0/blob/5969290b3f1710910c7cecdad6a34b2016fad9e7/lib/entitydef/entity_description.cpp server_programming_guide.pdf
  11. совсем наглея Таки ещё раз предлагаю опенсорснуться, пока интереснейшая тема совсем не заглохла
  12. Учитывая что сервер собирается под linux, я очень удивился бы наличию sln
  13. Вы меня ещё больше запутали Понятное дело что ВГшные правки не утекали. В изначальном сообщении говорилось что исходников сервера (т.е. даже "ванильных") нет даже в древнем сливе bw191(?), а во втором @Dragon armor упоминает что есть только линуксовые бинари. Но в то же время я их прямо сейчас наблюдаю в виде сорсов.
  14. А в тех что я кидал разве серверной части нет?
  15. Что-то все разбежались... а опенсорснуть свои достижения никто не желает?
  16. Что-то мне подсказывает что все же физ. движок если не захлебнется от такого количества моделей, то как минимум начнет подтормаживать. И в итоге придется сначала писать препроцессор, который поубирает лишние полигоны.
  17. XXXXZZZZ[o|i].chunk, каждый 100х100 попугаев. В питоне есть метод BigWorld.findChunkFromPoint(point, spaceID), можешь проверить, но емнип ты правильно написал. spaceID можно из какой-нибудь entity вытянуть
  18. @Dragon armorпроверил танки, тут это пакет 0x24 в реплее, используется для обновления destroyedFragiles/fallenColumns/fallenTrees/damageStickers/других list/dict . ...или вот DEBUG:root:nested property request for id=10261187 isSlice=True data=ef0093bda506 DEBUG:root:next path item: fallenTrees(3) DEBUG:root:object: [[0, 141, 189, 112, 163], [0, 145, 189, 113, 58], [0, 146, 189, 128, 30]] <class 'def_generator.nested_types.PyFixedList'> DEBUG:root:Bits per array index: 2 DEBUG:root:List index: 3 DEBUG:root:Slice index: 3 DEBUG:root:new list object: [[0, 147, 189, 165, 6]] DEBUG:root: DEBUG:root: DEBUG:root:nested property request for id=10912843 isSlice=True data=e3c00802ea66e2ed5d87 DEBUG:root:next path item: damageStickers(12) DEBUG:root:object: [11992011582228136455L, 4675071338421158407L, 5466597707613143559L] <class 'def_generator.nested_types.PyFixedList'> DEBUG:root:Bits per array index: 2 DEBUG:root:List index: 3 DEBUG:root:Slice index: 3 DEBUG:root:new list object: [9754213924599366152L] Те же три пустых байта в пакете, тот же формат. self.entity_id, = struct.unpack('I', stream.read(4)) self.is_slice = struct.unpack('b', stream.read(1))[0] == 1 self.payload_size, = struct.unpack('b', stream.read(1)) self.u = stream.read(3) # unknown self.payload = stream.read() Так что если будут с ним проблемы - пиши, попробую помочь.
  19. Так вот без контекста сложно сказать... причешу код для корабельных реплеев и опопробую то же самое на танковых, тогда смогу сказать точно. К примеру, разбор одного из упакованных путей для изменения свойства entity.state['resources'][0]['amountByEntities'][2]. 1010 1110 0111 1110‬ | AE 7E + PAYLOAD len(entity) == 11 => 4 bytes for entityPropertyId ‭1 0101 => entityPropertyId = 5 => state len(entity.state) == 16 keys in dict => 4 bytes 1 1001 => PropertyId = 9 => resources //resources - под индексом 9 в .def файле len(entity.state['resources']) == 1 item in array => 0 bytes 1 => PropertyId = 0 => resorces[0] len(entity.state['resources'][0]) == 2 keys in dict => 1 bytes 1 1 => PropertyId = 1 => resources[0]['amountByEntities'] //amountByEntities - под индексом 1 в .def файле len(entity.state['resources'][0]['amountByEntities']) == 3 items in array => 2 bytes 1 10 => PropertyId = 2 => resources[0]['amountByEntities'][2] 0 => exit while Object by path: {'current': 0, 'max': 550, 'id': 550, 'min': 0}, len = 4 => 3 bytes 001 => 1 => current
  20. Именно что 3 байта, не знаю их предназначение но они всегда по нулям.. Да, это оно, а obj.childLength() - как раз текущее кол-во свойств (размер массива / кол-во ключей в словаре / кол-во свойств в entity).
  21. @Dragon armor, короче разобрался с этим кошмаром, австралийские(?) разрабы решили там зачем-то экономить каждый бит(!). Определить setNested/setSlice можно по флагу в пакете, он сразу после entityId лежит. Потом лежит размер, 3 байта 0x00 и, собственно, данные (по крайней мере в реплее). Что setNested что setSlice начинаются с упакованного пути к изменяемому элементу, читать приходится побитово. Псевдокодом будет примерно так. pOwner = entity while bits.pop(0) == 1 && pOwner length = obj.childLength() // длина массива || кол-во элементов в словаре || для entity - кол-во свойств bits = bitsRequires(length) // смотрим сколько бит нужно дабы уместить индекс для контейнера выше => ceil(log(length, 2)) propertyId = readBits(bitsArray, bits) // читаем нужное кол-во бит, недостающее добиваем нулями до "целого" байта, переводим в int32 pOwner = pOwner.getPropertyByIndex(propertyId) // для массива - берем по индексу элемент, для словаря - по порядковому номеру свойство в .def файлике, для entity - по exposedId Потом (уже вне цикла while) начинаются отличия, для setNested читаем индекс изменяемого свойства как обычно: maxBits = bitsRequires(pOwner.childLenght()) propertyId = readBits(bits, maxBits) Всё, остаток информации можно распаковать используя тип данных из def/alias. Для setSlice чуть иначе maxBits = bitsRequires(pOwner.childLenght() + 1) index1 = readBits(bits, maxBits) index2 = readBits(bits, maxBits) // если индекс превышает кол-во элементов в pOwner -> добавляем новый // если index2 > index1 && payload == None -> удаляем из массива // замены, похоже, выполняются через setNested В общем-то ничего особо сложного когда разобрался, но мозг мне этот пакет выносил долго...
×
×
  • Create New...