Author: Vladimir Chebotarev aka ex-lend
При перетаскивании вещи клиент посылает серверу пакет типа:
22 XX XX YY ZZ QQ QQ WW WW RR RR, где: XX XX - id юнита YY - место, откуда (1 - тело, 2 - инвентарь, 4 - прилавок, 5-8 - полки) ZZ - место, куда QQ QQ - номер слота, откуда WW WW - номер слота, куда RR RR - количество |
Здесь надо заметить, что сервер не делает проверки при перетаскивании с прилавка в инвентарь, это запрещено только с клиентской стороны. Таким образом, мы можем перетаскивать вещи с прилавка бесплатно и без лишних усилий.
Посмотрим на код, генерирующий пакет перетаскивания вещи (allods2.exe):
.text:0041C087 mov eax, [ebp+var_8] .text:0041C08A mov ecx, [eax+138h] .text:0041C090 mov edx, [ebp+var_4] .text:0041C093 mov ax, [ecx+4] .text:0041C097 mov [edx+0Ah], ax .text:0041C09B mov ecx, [ebp+var_4] .text:0041C09E mov dx, [ebp+arg_4] .text:0041C0A2 mov [ecx+0Eh], dx .text:0041C0A6 mov eax, [ebp+var_4] .text:0041C0A9 mov cl, [ebp+arg_0] .text:0041C0AC mov [eax+0Ch], cl ; место, откуда .text:0041C0AF mov edx, [ebp+var_4] .text:0041C0B2 mov ax, [ebp+arg_C] .text:0041C0B6 mov [edx+10h], ax .text:0041C0BA mov ecx, [ebp+var_4] .text:0041C0BD mov dl, [ebp+arg_8] .text:0041C0C0 mov [ecx+0Dh], dl ; место, куда .text:0041C0C3 mov eax, [ebp+var_4] .text:0041C0C6 mov cx, [ebp+arg_10] .text:0041C0CA mov [eax+12h], cx |
Нетрудно модифицировать его таким образом, например, чтобы при перемещении с прилавка (4) куда-бы то ни было пункт назначения был установлен в инвентарь (2).
Для защиты от этой ошибки необходимо ввести дополнительную проверку на сервере. Рассмотрим код обработки пакета 22 (a2server.exe):
.text:00505A85 loc_505A85: ; CODE XREF: sub_504A96+F95 .text:00505A85 mov edx, [ebp+var_5C] .text:00505A88 xor eax, eax .text:00505A8A mov al, [edx+0Ch] ; место - откуда .text:00505A8D cmp eax, 4 ; прилавок? .text:00505A90 jnz short loc_505AD8 .text:00505A92 mov ecx, [ebp+var_54] .text:00505A95 mov edx, [ecx+10h] .text:00505A98 push edx .text:00505A99 mov ecx, [ebp+var_C38] .text:00505A9F call sub_502C50 .text:00505AA4 mov [ebp+var_6C], eax .text:00505AA7 cmp [ebp+var_6C], 0 .text:00505AAB jnz short loc_505AB2 .text:00505AAD jmp loc_50864E .text:00505AB2 ; --------------------------------------------------------------------------- .text:00505AB2 .text:00505AB2 loc_505AB2: ; CODE XREF: sub_504A96+1015textj .text:00505AB2 mov eax, [ebp+var_5C] .text:00505AB5 xor ecx, ecx .text:00505AB7 mov cx, [eax+12h] .text:00505ABB push ecx .text:00505ABC mov edx, [ebp+var_5C] .text:00505ABF movsx eax, word ptr [edx+0Eh] ; номер слота - откуда .text:00505AC3 push eax .text:00505AC4 mov ecx, [ebp+var_54] .text:00505AC7 push ecx .text:00505AC8 mov ecx, [ebp+var_6C] .text:00505ACB call sub_5446C7 ; вытащить вещи с прилавка в eax .text:00505AD0 mov [ebp+var_60], eax .text:00505AD3 jmp loc_505BDF |
Функция по адресу 5446C7 перемещает вещь с заданного слота прилавка во временный объект и возвращает его, а далее этот объект независимо от происхождения может быть перемещен в любой пункт назначения.
Чтобы найти нужную нам вещь переберем все вещи на прилавке. Далее посмотрим ее происхождение. Если вещь магазинная, вместо объекта с вещью надо вернуть 0 и не выполнять никакого перемещения. Этот код надо вставить вместо вызова sub_5446C7:
push ebp mov ebp, esp push ecx mov [ebp-0x04], ecx mov eax, [ebp-0x04] mov ecx, [eax+6Ch] mov eax, [ebp+0x08] push eax call 0x547468 ; получим список вещей прилавка lea ecx, [eax + 0x078] mov eax, [ecx + 4] ; число вещей на прилавке test eax, eax jz ret0 mov edx, [eax + 8] mov eax, [eax] xor ecx, ecx start: test edx, edx jz ret0 cmp ecx, [ebp + 0x0c] ; сравниваем индекс вещи с номером слота jnz next cmp dword ptr [edx + 0x14], 0 ; ключевой момент jz ret0 ; магазинная вещь, выходим без перетаскивания jmp done next: add ecx, 1 ; перейдем к следующему элементу связного списка test eax, eax jz ret0 mov edx, [eax + 8] mov eax, [eax] jmp start ret0: xor eax, eax mov esp, ebp pop ebp retn 0Ch done: mov eax, [ebp+0x010] push eax mov ecx, [ebp+0x0C] push ecx mov edx, [ebp+0x08] push edx mov eax, [ebp-0x04] mov ecx, [eax+6Ch] call 0x547C5A ; вызовем извлечение вещи из инвентаря ; это все что было внутри ; старой sub_5446C7 mov esp, ebp pop ebp retn 0Ch |