среда, 22 января 2014 г.

WoT Marker (поддержите огнем по.... )

Играя в ротных боях и стреляя по фокусу или маркеру (кому как удобно) зачастую сам не успеваешь или забываешь или не попадаешь по кнопке маркера (по умолчанию "T"). А маркер ставить надо:
во 1ых командир видит кто куда стреляет
во 2ых маркер должен гореть пока противник жив

Об этом конечно можно поспорить, мол чат забивается итд, 5ое, 10ое речь не об этом. В некоторых отладочных случаях может понадобиться ставить маркер вместе с выстрелом. Настроить это обычным способом не получится (Клиент игры не дает устанавливать выстрел и маркер на LMB), через изменение XML файла настроек изменить можно:
Файл:
 %appdata%\Wargaming.net\WorldOfTanks\preferences.xml
Секции:
<CMD_CM_SHOOT> <fireKey> KEY_LEFTMOUSE </fireKey> <satelliteKeys> </satelliteKeys></CMD_CM_SHOOT>
<CMD_CHAT_SHORTCUT_ATTACK>
<fireKey> KEY_T </fireKey>
<satelliteKeys> </satelliteKeys>
</CMD_CHAT_SHORTCUT_ATTACK>
 но не работает... как же быть?

лично я сделал следующее:
установил виндовый хук на левую кнопку мыши и в callback функции отправляю окну игры оконное сообщение (PostMessageA) с нажатием на кнопку маркера которая настроена в клиенте. Таким не хитрым образом вместе с выстрелом одновременно ставится маркер.

Код:

HookDLL.cpp
#####################################################################
#include "stdafx.h"
#include "HookDLL.h"

#pragma data_seg(".Segment")
HWND hWndServer = NULL;
UINT UWM_MOUSELBUTTONDOWN;
unsigned long iMessageDown=0, iMessageUp=0, iWparamDown=0, iLparamDown=0, iWparamUp=0, iLparamUp=0;
#pragma data_seg()
#pragma comment(linker, "/section:.Segment,rws")

HINSTANCE hInst;

HHOOK hook;

BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
switch( ul_reason_for_call )
    {
case DLL_PROCESS_ATTACH:
hInst = hModule;
UWM_MOUSELBUTTONDOWN = ::RegisterWindowMessage(UWM_MOUSELBUTTONDOWN_MSG);
break;
default:
break;
}
    return TRUE;

}


static LRESULT CALLBACK MouseMsgProc(UINT nCode, WPARAM wParam, LPARAM lParam);


__declspec(dllexport)  BOOL InstallHook( HWND hWndParent, unsigned long MESSAGEDOWN, unsigned long MESSAGEUP, unsigned long WPARAMDOWN, unsigned long LPARAMDOWN, unsigned long WPARAMUP, unsigned long LPARAMUP )
{

if(hWndServer != NULL)
return FALSE; // already hooked!
hook = SetWindowsHookEx( WH_GETMESSAGE, (HOOKPROC)MouseMsgProc, hInst, 0);
if(hook != NULL)
{
iMessageDown = MESSAGEDOWN;
iMessageUp = MESSAGEUP;
iWparamDown = WPARAMDOWN;
iLparamDown = LPARAMDOWN;
iWparamUp = WPARAMUP;
iLparamUp = LPARAMUP;

hWndServer = hWndParent;
return TRUE;
}
return FALSE;
}

__declspec(dllexport) BOOL UnInstallHook( HWND hWndParent )
{
if(hWndParent != hWndServer || hWndParent == NULL)
return FALSE;
     BOOL unhooked = UnhookWindowsHookEx(hook);
     if(unhooked)
hWndServer = NULL;
     return unhooked;
return TRUE;
}


static LRESULT CALLBACK MouseMsgProc(UINT nCode, WPARAM wParam, LPARAM lParam)
{

if(nCode < 0)
{
CallNextHookEx(hook, nCode, wParam, lParam);
return 0;
}
LPMSG msg = (LPMSG)lParam;
switch( msg->message  )
{

case WM_LBUTTONDOWN:
::PostMessageA(hWndServer, iMessageDown, iWparamDown, iLparamDown);
::PostMessageA(hWndServer,  iMessageUp, iWparamUp, iLparamUp);

break;
case WM_NCLBUTTONDOWN:
::PostMessageA(hWndServer, iMessageDown, iWparamDown, iLparamDown);
::PostMessageA(hWndServer,  iMessageUp, iWparamUp, iLparamUp);
break;

default:
break;
}
return CallNextHookEx(hook, nCode, wParam, lParam);
}




HookDLL.h
############################################################
#ifndef _MOUSE_EVENT_RECORD_H_
#define _MOUSE_EVENT_RECORD_H_
#define LIBSPEC __declspec(dllexport)

   LIBSPEC BOOL InstallHook( HWND hWndParent, unsigned long MESSAGEDOWN, unsigned long MESSAGEUP, unsigned long WPARAMDOWN, unsigned long LPARAMDOWN, unsigned long WPARAMUP, unsigned long LPARAMUP );
   LIBSPEC BOOL UnInstallHook(HWND hWnd);
#undef LIBSPEC

#define UWM_MOUSELBUTTONDOWN_MSG ( L"UWM_MOUSELBUTTONDOWN_USER_MSG" )

#endif




WoTAutoMarker.cpp
###############################################################

#include "stdafx.h"
#include "Windows.h"
#include "conio.h"
#include "HookDLL.h"
#include <iostream>
#include <fstream>
#include <string>

#pragma comment(lib, "HookDLL.lib")

int _tmain(int argc, _TCHAR* argv[])
{
  unsigned long MESSAGEDOWN;
  unsigned long MESSAGEUP;
  unsigned long WPARAMDOWN;
  unsigned long LPARAMDOWN;
  unsigned long WPARAMUP;
  unsigned long LPARAMUP;
  std::string line;

  std::ifstream fconfig ("config.txt");
  if (fconfig.is_open())
  {
    std::getline (fconfig, line);
    MESSAGEDOWN = stoul(line);
std::cout << "MESSAGEDOWN: " << line << "\n";
std::getline (fconfig, line);
    MESSAGEUP = stoul(line);
std::cout << "MESSAGEUP: " << line << "\n";

std::getline (fconfig, line);
    WPARAMDOWN = stoul(line);
std::cout << "WPARAMDOWN: " << line << "\n";

std::getline (fconfig, line);
    LPARAMDOWN = stoul(line);
std::cout << "LPARAMDOWN: " << line << "\n";

std::getline (fconfig, line);
    WPARAMUP = stoul(line);
std::cout << "WPARAMUP: " << line << "\n";

std::getline (fconfig, line);
    LPARAMUP = stoul(line);
std::cout << "LPARAMUP: " << line << "\n";
fconfig.close();
  }

  else {
 std::cout << "Unable to open config.txt file"; 
 getch();
 return 0;
  }



HWND hWnd = ::FindWindowEx(NULL,NULL,L"App", NULL);
printf("hWnd: %Xh\n", hWnd);
if ((int)hWnd == 0) {
printf("please launch WORLDOFTANKS.EXE and try again...\nAny key to exit");
getch();
return 0;
}

if (InstallHook(hWnd, MESSAGEDOWN, MESSAGEUP, WPARAMDOWN, LPARAMDOWN, WPARAMUP, LPARAMUP))
printf("mouse hook install\n");
else
printf("mouse hook NOT install. try again later...\n");
printf("press any key to exit after game.\n");
getch();

UnInstallHook(hWnd);
    return 0;
 
}

Видео о там как это работает
Для тех кому лень возиться с компилятором:
скачать


вторник, 16 июля 2013 г.

содержание wotreplay файлов

и так...
с заголовками и распаковкой (zlib) и расшифровкой (blowfish) все ясно, что же за данные в итоге получаются?

в процессе отладки не сложно найти место распаковки/расшифровки после чего в памяти можно найти область памяти с сырыми данными, идентичными распакованному файлу. Поставив бряк на чтение из этой области памяти найдется вот такой вызов memcpy, который и читает все получившиеся данные:

0093294E |. 50 PUSH EAX ; /n
0093294F |. 03CA ADD ECX,EDX ; |
00932951 |. 51 PUSH ECX ; |src
00932952 |. 56 PUSH ESI ; |dest
00932953 |. E8 547A3B00 CALL <JMP.&MSVCR90.memcpy> ; \memcpy

Далее написав небольшой скрипт для отладчика ollydbg сохраняем в лог интересующие нас данные, и сохраняем лог в файл для дальнейшего анализа:

var b // байты копируемые memcpy
var n // sizeof(b)
bp 00932953 //бряк на memcpy
eob llog
run
coe

nextbp:
run

llog:
log eax
log ecx
log esi
mov n, eax
mov b, [ecx], n
logbuf b, n
jmp nextbp

посмотрев на получившиеся данные, очень хорошо видно, что абсолютное большинство чтений из файла это данные по 12 байт, при этом сами данные часто повторяются или имеют небольшие отличия.

"0000000C";"7A300020";"0012EEC0";"21 00 00 00 14 00 00 00 00 00 00 00 "
"00000004";"7A30002C";"0012EE6C";"1D 00 00 00 "
"0000001D";"7A300030";"0ADB1FC8";"57 6F 72 6C 64 C2 A0 6F 66 C2 A0 54 61 6E 6B 73 20 76 2E 30 2E 38 2E 34 20 23 33 39 39 "
"0000000C";"7A30004D";"0012EEC0";"01 00 00 00 0E 00 00 00 00 00 00 00 "
"0000000C";"7A30005A";"0012EEC0";"3E 00 00 00 00 00 00 00 00 00 00 00 "
"0000000C";"7A3000A4";"0012EEC0";"BD 07 00 00 08 00 00 00 00 00 00 00 "
"0000000C";"7A30086D";"0012EEC0";"45 01 00 00 08 00 00 00 00 00 00 00 "
"0000000C";"7A3009BE";"0012EEC0";"2E 00 00 00 08 00 00 00 00 00 00 00 "
"0000000C";"7A3009F8";"0012EEC0";"2F 00 00 00 01 00 00 00 00 00 00 00 "
"0000000C";"7A300A33";"0012EEC0";"05 00 00 00 02 00 00 00 00 00 00 00 "
"0000000C";"7A300A44";"0012EEC0";"6A 00 00 00 0B 00 00 00 00 00 00 00 "
"0000000C";"7A300ABA";"0012EEC0";"13 00 00 00 0B 00 00 00 00 00 00 00 "
"0000000C";"7A300AD9";"0012EEC0";"31 00 00 00 0A 00 00 00 00 00 00 00 "
"0000000C";"7A300B16";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300B2E";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300B46";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300B5E";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300B76";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300B8E";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300BA6";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300BBE";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300BD6";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300BEE";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300C06";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300C1E";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "
"0000000C";"7A300C36";"0012EEC0";"0C 00 00 00 03 00 00 00 00 00 00 00 "

.......
.......
.......

Покопавшись в python скриптах WoT клиента и исходниках BW191
можно предположить, что это координаты объектов, камер, и т.д.
Vector3::Vector3 (
float a,
float b,
float c
)

так же в исходниках  BW191 есть интересные структуры waypoint, но используются ли они в WoT сказать не могу, кому интересно сами найдут :) Всем удачи.

вторник, 7 августа 2012 г.

WoT сохраненные пароли (saved passwords)

Клиент игры умеет  сохранять пароль от учетной записи локально на компе.
Если посмотреть в файл "%AppData%\wargaming.net\WorldOfTanks\preferences.xml" и поискать там секцию:
<logininfo>
          <host>    auto.login.app:0000    </host>            <rememberpwd>    true    </rememberpwd>

            <peripherylifetime>    KEkxC********************</peripherylifetime>

            <pwd>  AQAAANCMnd8BFdERjHoAwE************************** </pwd>

            <login>  AQAAANCMnd8BFdERjHoAwE************************** </login>
 </loginInfo>

можно заметить сохраненные логин и пароль, но только они естественно не plaintext :) как многие бы хотели.

О том что это за строки такие хитрые любопытные читатели могут посмотреть тут: link
ну или тут для плохо знающих инглиш: link

IT сувениры

воскресенье, 24 июня 2012 г.

python pickle search (WoTReplay files)

провел небольшой анализ распакованных файлов реплеев.
В этих файлах содержаться сохраненные pickle данные их примерно(!) можно найти по сигнатуре 80 02
я решил  читать файл со смещением один байт и пробовать загрузить pickle данные, чтобы найти все сохраненные таким образом объекты.

import pickle
def unpickle(file):
    f = open(file,'rb')
    g = 0
    seg = f.tell()
    bb = f.read()
    while bb != b'' :
        seg += 1
        f.seek(seg+1)
        try:
            o = pickle.loads(bb)
            g += 1
            print g, hex(int(seg)-2)
            print o print "\n---------------------\n"
        except:
            pass
        bb = f.read()
unpickle("unpack.wotreplay")
Этот python скрипт будет искать все pickle данные в файле. про сигнатуру выше написано не совсем верно.

четверг, 14 июня 2012 г.

WoT Replay unpacker

Небольшая программка для распаковки replay файлов.
simple console application for unpack Worlds of Tank Replay Files.
download wotreplay_unpacker
Распакованный replay файл проигрывается клиентом игры!!!


понедельник, 11 июня 2012 г.

WoT Replay format

есть такая замечательная игра World of Tanks (0.7.3) Клиент игры умеет записывать реплеи боев. Во время боя создается файл (temp.wotreplay ???) так вот пока бой не закончен файл можно скопировать (потом пригодится :)) как только бой окончен начинаются чудеса: файл сжимается и переименовывается :(
Про просмотре hex редактором полученного файла в нем явно видно две части:
- 1ая текстовые данные о бое в JSON формате (данных о бое много: все ники игрков, команды, кланы, дамаг кадого, прибыль за бой и т. д.)
- 2ая явно выраженный архив.

посмотрев в отладчик можно заметить что 2ая часть это не просто архив, а зашифрованный (BLOWFISH) архив.

Собственно ключ (BF_KEY):
DE 72 BE A0 DE 04 BE B1 DE FE BE EF DE AD BE EF

процесс восстановления выглядит так blowfish - XOR (!!!) - zlib
расшиврованные данные надо ещё поксорить а потом расжать zlib'ом
В итоге получим файл идентичный тому временному файлу который записывался во время боя.

Что не может не радовать - так это то что расшиврованный / распакованный файл проигрывается клиентом успешно без лишних манипуляций :)

Посмотрев hex редактором на этот файл можно заметить заголовловок и дамп python объектов (pickle) которые представляют собой объекты игры и их сценарии поведения.

--------------------------------------------------------------------------------------------------
void BF_set_key(BF_KEY *key, int len, const unsigned char *data);
--------------------------------------------------------------------------------------------------
00CA77EE $-FF25 B853E600 JMP DWORD PTR DS:[<&LIBEAY32.#46>] ; LIBEAY32.BF_set_key
STACK:
0012EE20 008A961F RETURN to WORLDOFT.008A961F from <JMP.&LIBEAY32.#46>
0012EE24 0C3BA778 -> BF_KEY *key
0012EE28 00000010 -> int len
0012EE2C 08B51170 -> const unsigned char *data

[PASSWD]
08B51170 DE 72 BE A0 DE 04 BE B1 DE FE BE EF DE AD BE EF ▐r╛á▐ ╛▒▐■╛∩▐¡╛∩


--------------------------------------------------------------------------------------------------
void BF_decrypt(BF_LONG *data,const BF_KEY *key);
--------------------------------------------------------------------------------------------------
0222A9E9 E8 6D040000 CALL LIBEAY32.BF_decrypt

STACK:
0012EE1C 0012EE24 -> BF_LONG *data
0012EE20 0C3BA778 -> *data,const BF_KEY *key
0012EE24 B45E3A69 ---- 1st 64 bit(8 byte) to decrypt