Дилетант і back-інжиніринг. Частина 1: Шляхи і файли

Як багато з прогеров робили проекти за мотивами ігор, в які вони грали? Думаю, що багато. Вам можуть говорити, що гра так собі, що не потрібно витрачати на безглуздий проект час, все одно він нікому не підійде, але вам без різниці, тому що це було круто, і хоч як-то хочеться повторити ті емоції.

В свій час я дуже багато грав у «Tales of pirates» (далі TOP) від Moli, точніше в її російську локалізацію від Nival-а — «Пиратию». Шикарна, як на мене, гра. Так, не WOW, але ж і я про нього нічого не знав. Минуло багато років, «Пиратию» закрили, я виріс, але в океані пам'яті все одно плаває забутий підкорювач морів на кораблику 54-го рівня.

image

Після того, як я дізнався, що закрили російський сервер, а я в той момент вже не грав пару років, знову прокинувся азарт. Через пару місяців я трохи розібрався в світі фан-серверів, намагався налаштувати свій на локалці, почав розбиратися з структурою файлів. І тут понеслася. Спочатку вирішив навчиться працювати з моделями. За пару годин знайшов на Pastebin якийсь код, який після напилка міг відкривати, правда з проблемами, модельки. Але крім моделей в грі були зашифровані текстури.

На щастя була одна програмка, Gemini Decompiler, вміє перетворювати текстури гри в нормальні і загальноприйняті картинки. Ще більше пощастило, що програма ця була написана на .Net. Після декомпилятора виявилося, що нормальна текстура розбивалася на 3 блоки — перші 44 байта, останні 44 байта, і інше — в зашифрованою текстурі спочатку йшли останні 44 байта, потім основна частина, потім перші 44 байта, і ще 4 байта неизвесно для чого.

На основі коду з Pastebin і перетворювача текстур вийшло зробити переглядач моделей. Жахливий звичайно, але все ж. Ось тільки читав він тільки найпростіші моделі, начебто мечів\посохов\кинджалів, персонажів і елементи декору без анімації.

image

А мені хотілося руху. І я почав шукати дізассемблер, щоб розібрати по шматочках бібліотеку движка TOP. Знайшов піратський IDA Pro. І почав ламати… Зручна взагалі програмка, особливо тим, що вона вміє адекватно розв'язати імена після обробки компілятором і переводить результат дизасембера на псевдокод, вкрай схожий на С. В статті про Caesar 3 про це згадувалося.

Я працював з перемінним успіхом близько двох місяців. Ну як працював, грався. Знайшов код з математичними операціями, почав засовувати структури з переглядача моделей в базу IDA, потім знайшов, що можна імпортувати .pdb (а він є, мабуть для зворотного зв'язку, якщо є помилки), правил код і структури, що видавалися конструктором псевдокода. Але відчувалося, що все не те. У мене ж є .pdb, а там, наскільки знав, є інформація про проект. Я знайшов купу різних програм, але всі вони видавали лише загальну інформацію. Тоді почав розглядати Debug Access Interface (далі DIA), якби ще він поставлявся з Express версією Visual studio. Коротше тепер я пишу Professional редакції. Скомпілювавши приклад dia2dump я офігів. У мене під рукою була купа службової інформації. Трохи більше 30 мегабайтів тексту. Наприклад там є список всіх .obj файлів, що згодовуються линковщику, крім того для кожного .obj файлу є список джерел, що туди входять. Загалом за один день був готовий код на створення файлів з проекту движка TOP. А далі будуть створення структур і підключення инкудов за .pdb…

Такі справи.

На закуску — додатковий код для dia2dump. Код для підрахунку кількості .obj в проекті, виведення файлів в кожному .obj і створення папок і файлів проекту з .pdb.
Відкрити результат роботи кодера
int total=0;

bool AQLCreateDirectory(WCHAR * sPathTo)
{
while(CreateDirectory(sPathTo, NULL) == FALSE)
{
WCHAR sTemp[MAX_PATH];
int k = wcslen(sPathTo);
wcscpy(sTemp, sPathTo);
while(CreateDirectory(sTemp, NULL) != TRUE)
{
while(sTemp[--k] != L'\\') 
{
if(k<=1) 
return FALSE;
sTemp[k] = NULL;
}
}
}
return TRUE;
};

void Process(IDiaSession *pSession, IDiaSymbol *pGlobal)
{
int total=0;
IDiaEnumSymbols *pEnumSymbols;
if (FAILED(pGlobal->findChildren(SymTagCompiland, NULL, nsNone, &pEnumSymbols)))
return;
IDiaSymbol *pCompiland;
ULONG celt = 0;

while (SUCCEEDED(pEnumSymbols->Next(1, &pCompiland, &celt)) && (celt == 1)) 
{
pCompiland->Release();
total++;
}
fwprintf(pFileout,L"%i\n", total);
pEnumSymbols->Release();
if (FAILED(pGlobal->findChildren(SymTagCompiland, NULL, nsNone, &pEnumSymbols)))
return;
celt = 0;

while (SUCCEEDED(pEnumSymbols->Next(1, &pCompiland, &celt)) && (celt == 1)) 
{
BSTR bstrName;
if (pCompiland->get_name(&bstrName) == S_OK) 
{
fwprintf(pFileout,L"%s\n", bstrName);
SysFreeString(bstrName);
}

int num=0;
IDiaEnumSourceFiles *pEnumSourceFiles;
if (SUCCEEDED(pSession->findFile(pCompiland, NULL, nsNone, &pEnumSourceFiles))) 
{
IDiaSourceFile *pSourceFile;
while (SUCCEEDED(pEnumSourceFiles->Next(1, &pSourceFile, &celt)) && (celt == 1)) 
{
num++;
pSourceFile->Release();
}
pEnumSourceFiles->Release();
fwprintf(pFileout,L"%i\n", num);
}

if (SUCCEEDED(pSession->findFile(pCompiland, NULL, nsNone, &pEnumSourceFiles))) 
{
IDiaSourceFile *pSourceFile;
while (SUCCEEDED(pEnumSourceFiles->Next(1, &pSourceFile, &celt)) && (celt == 1)) 
{
BSTR bstrSourceName;
if (pSourceFile->get_fileName(&bstrSourceName) == S_OK) 
{
fwprintf(pFileout,L"%s\n", bstrSourceName);

WCHAR *path = new WCHAR[wcslen(bstrSourceName)+8];
wcscpy(path,L"c:\\test\\");
wcscat(path,bstrSourceName+2);
WCHAR *filename = new WCHAR[wcslen(path)+1];
wcscpy(filename,path);
for(int k=wcslen(path)-1;k>=0&&path[k]!=L'\\';k--)path[k]=0;

bool ok = AQLCreateDirectory(path);
FILE *file = _wfopen(filename,L"w");
fclose(file);
delete(filename);
delete(path);

SysFreeString(bstrSourceName);
}
pSourceFile->Release();
}
pEnumSourceFiles->Release();
}
pCompiland->Release();
}
pEnumSymbols->Release();
}



Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.