Фикс воровства предметов из магазина

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