![]() |
![]() |
|
Документация Различные статьи и мануалы по установке, настройке и редактированию серверной и клиентской части игры Perfect World. |
![]() |
Опции темы |
![]() |
#1 |
Изгнанные
|
![]()
Вступление
Здесь я постараюсь объяснить суть работы официального патчера ПВ. Если вы пробовали бету моего апдейтера, заснифирить, углядеть или что-то еще, что вам не помогло, вам будет как минимум полезно. Если смотреть официальную документацию, то у каждого оф-сервера есть 4 сервера для патчей. patch1— тот куда заливают непереведенные патчи китайцы, patch2 — туда наши заливают перевод, чтобы китаёзы сделали патч, patch4 — сюда китаёзы заливают непосредственно патч для тестов. Если все с этим патчем хорошо, то он перемещается с patch4 на patch3, откуда все пользователи его благополучно и скачивают. Лично нам интересен момент когда китайцы делают патч для тестов. Т.к. тестить-то нам, пряморуким ходячим хомосапиенсам не надо, мы после него сразу на 4 пункт переходим ![]() Структура Для того, чтобы сделать патчер/патч нам нужны несколько вещей: умение делать цифровую подпись файлов с помощью связки открытого и закрытого ключа, умение подсчитывать проверочные суммы файлов с помощью алгоритма md5, умение пользоваться base64 с пользовательским словарем и знакомство с алгоритмом/либой для упаковки zlib. Рассмотрим файлы, необходимые для обновления с обоих сторон. Те кто пытались достучаться до сервера обновлений ру(май/ки и других)-офа, могли заметить следующию структуру: — patch3.pwonline.ru/CPW/ —— pid —— patcher —— element —— launcher Эти папки — основа для любого клиента любой игры созданной Perfect World Ltd. Каждая такая папка, кроме pid, состоит из одной подпапки с файлами с аналогичным названием (т.е. есть папка patch3.pwonline.ru/CPW/patcher/patcher/), в которой содержаться упакованные файлы для загрузки, и файлами для проверки: ——— version — файл, в котором содержится номер текущей версии на сервере; ——— files.md5 — файл со списком всех-всех файлов клиента, с md5-суммами и цифровой подписью внизу; ——— v-X.inc, где X — разница между версией на сервере и версией клиента — файлы, в которых содержаться только пути к изменившимся/добавленным/удаленным файлам игры, их md5-сумма и цифровая подпись. Папка pid содержит единственный файл info. В клиенте ПВ два уровня патчера: 1) Perfect World/launcher/launcher.exe — та мини-программа, которая лезет на сервер обновлений только для того, чтобы узнать не нужно ли обновлять себя и программу из пункта 2; 2) Perfect World//patcher/patcher.exe — основной патчер. да-да, это тот, что качает кусочки упакованного содержимого pck-файлов, карты, списки серверов, словом все что в папке element. Конфиги с номером текущей версии клиента и ошибками, произошедшими во время обновления, хранятся в корне клиента в папке Perfect World/config/. Вариант первый, простой запуск через launcher.exe. 1) первым делом launcher.exe сверяет /pid/info с Perfect World/patcher/server/pid.ini: — если содержимое одинаковое, то ланчер идет на пункт 2; — если нет, то выдаст какую-то ошибку. Попробуйте кто-нибудь, я уже не помню. 2) далее, ланчер качает /patcher/version и сверяет с Perfect World/config/patcher/version.sw: — если версия клиента отстает от серверной, то происходит процесс обновления, о котором будет сказано отдельным абзацем, а потом запуск патчера; — если версии совпадают, то происходит запуск патчера; — если версия на сервере меньше клиентской вылетит ошибка, смысл которой будет, что сервер временно не доступен. 3) запускается патчер. Проверяет pid, а затем он качает /element/version и сверяет с Perfect World/config/element/version.sw: — если версия клиента отстает от серверной, то происходит процесс обновления, о котором будет сказано отдельным абзацем, а потом запуск патчера; — если версии совпадают, то происходит запуск патчера; — если версия на сервере меньше клиентской вылетит ошибка, смысл которой будет, что сервер временно не доступен. Да, Ctrl-C, Ctrl-V. Вариант второй, полная проверка и запуск через Perfect World/launcher/FixIt.bat. 1) launcher.exe запускается с ключом полной проверки FullCheck. Он проверяет все файлы с серверными (об этом так же чуть позже) и заменяет отличающиеся файлы из папки патчера и своей на серверные. Тем не менее, проверка /pid/info с Perfect World/patcher/server/pid.ini так же осуществляется! 2) запускается патчер, который поступит совершенно так же как и ланчер, только для папки elements, если нажать кнопку «Проверка». 1. Обычное обновление Для примера будем считать, что ланчер этот этап прошел. ![]() Как мы уже выяснили, патчер уже знает версию сервера и клиента. Посчитав разность и отставание версии клиента, он стучится к файлам v-X.inc . Допустим, у меня свежий клиент с обновлением 73. Но сегодня нивал выпустил патч 74, поэтому патчер качает файлик http://patch3.pwonline.ru/CPW/element/v-1.inc Его содержимое: Код HTML:
# 73 74 118 !3007c149c0ffc0785fc21dce583ede62 /aW50ZXJmYWNlcw==/bG9hZGluZy5zdGY= -----BEGIN ELEMENT SIGNATURE----- iWp9dTEEyZuQFVp/DiDNdCVWJbCb6dn3rbz0CImfMfSxnp9XXawQygcS9JkBIPAw K05VnT+rCXXKGcNrlmxZwWx5tkY2iYlCidOltJsD1YBqbt+Ck7 1v1r07rvJp9lVW yjS0zizU0/JW3rJfofBAkIin2wQjGhSSiA+IE2PIbNc= Код HTML:
# ClientVersion ServerVersion BytesToDownload FLAGmd5sum PathToFile -----BEGIN ELEMENT SIGNATURE----- SIGNKEY ServerVersion — версия сервера; BytesToDownload — придется скачать ровно столько байт; FLAG — состояние файла. Нам известны два типа флагов — добавлен (+), изменен (!), но, вероятно, есть и удаление; md5sum — md5-сумма файла, перед закачкой клиент сверится, т.к. возможно вы уже пытались обновится и у вас произошел обрыв; PathToFile — путь к файлу относительно корневой директории. Если путь начинается со слеша (/), то это путь относительно корневой директории, если нет, то относительно текущей. У первого файла слеш точно должен быть! Сам путь получается в очень забавном виде: делается base64_encode (base64.ru для опытов) для каждой папки до файла и самого файла, в массиве этих строк для каждой строки делается замена слеша (/) на минус (-), потом объединяется с помощью тех же слешей в путь. Для распакованных паков опускается pck на конце. Приведу пример: файл из пака interfaces.pck/loading.stf, который как раз в этом обновлении, патчер будет качать по адресу patch3.pwonline.ru/CPW/element/element/aW50ZXJmYWNlcw==/bG9hZGluZy5zdGY=. Файл по этому адресу упакован смешанным алгоритмом с помощью zlib. SIGNKEY — подпись файла (именно файла v-1.inc в нашем случае) с помощью закрытого ключа с серверной стороны. Эта подпись проверяется клиентом у которого свой открытый ключ (он прописан в launcher.exe и patcher.exe) до загрузки файлов, и если результат будет отрицательным — загрузки файлов не будет! Подпись осуществляется для текста с самого начала файла до конца списка файлов. После расчета подписи, патчер на сервере дописывает в конец следующее: Код HTML:
\n-----BEGIN ELEMENT SIGNATURE-----\n ПОДПИСЬ_РАЗБИТАЯ_ПО_64_СИМВО ЛА_НА_СТРОКУ ![]() 2. Полная проверка А тут для примера будем думать, что мы запустили ланчер с помощью FixIt (полная проверка патчера, в общем). От автора: вы не подумайте что не хочу для element рассмотреть, просто размещать здесь 5 мегабайт текста не хочется ![]() В этом случае, ланчер качается полный список файлов (files.md5) и проверяет отличающиеся файлы. Сначала он скачает для себя http://patch3.pwonline.ru/CPW/launcher/files.md5 Его содержимое: Код HTML:
# 2 0445fb1b766e098bd767285a880cd553 /Zml4aXQuYmF0 6011e90ea67d338fbb399560b5ea7f1a bGF1bmNoZXIuYm1w f4a55cd348d4ca26ec588be3a98c5734 bGF1bmNoZXIuZXhl 90f6235a3bb787828ba01efd67ad841d c3RyaW5ndGFiLnR4dA== e40ef2ebd497d0a15421658643bd05c6 cGFja2RsbC5kbGw= 344a159d62deb21acb18313c96b24d1c dW5pY293cy5kbGw= -----BEGIN ELEMENT SIGNATURE----- Z5aRDLdAR6XSCtwMYu6y3FQtINfxn2kxB2YsiaElirAJOFkbUa f7t5cB6K50yUZv o4mIt6LgOHW4wvKrzX6zY3dVrbJu+z+fDLuRmckJbnPu8HzGLn vzgP3hnIvSHEGP +hnpl58SkkMYaHCJXd+2bpFwOR8WFsDXlCYDx6dHI+k= Код HTML:
# 5 80ccae5334d030fa9da7aafa60125189 /c2VydmVy/cGlkLmluaQ== 0b9a282d2fd208716162e1292268ffbb dXBkYXRlc2VydmVyLnR4dA== 44e9d53e2a1b869d49a074a054b8c866 /c2tpbg==/aW1hZ2U=/Y29tYm8tYmcuYm1w 797bf5ee4384b5ef246dca080e1051f3 Y29tYm8tZXhwYW5kYmcuYm1w 7d12b5ae21a001a043d6a6ef482ef309 Y29tYm8tZm9jdXNlZC5ibXA= 6ee841d62f57e4ac5a533df9a86375e9 Y29tYm8taG92ZXIuYm1w b50d2b0b6d3ab004c4ed397b332c8e5e Y29tYm8tbWFwLmJtcA== 1c491d0bd38d197c314d2d47a75117f6 Y29tYm8tc2VsZWN0ZWQuYm1w b6eac91f513b61c4735302134f936230 Y29tYm9zbWFsbC1iZy5ibXA= 7a2fcffd7d9c39362009cc15a09f8d58 Y29tYm9zbWFsbC1leHBhbmRiZy5ibXA= [...здесь пропущено много строк...] 9f4ec0abc9b7217ee7b26d19714eb38b OTcwMC5pbmk= 11a4017214ce0ade915d32b4c4bc561b Z2Vmb3JjZTJfNDAwLmluaQ== 0c73592438b247d9bf352e1bcfd4a09c Z2Vmb3JjZTYyMDAuaW5p e0cf04af20b21f2d55f73b71170c47fe dG50Mi5pbmk= -----BEGIN ELEMENT SIGNATURE----- jTJK8aW9bZBcV9DK8syw8AUwcVMAiJKl2Gy0fzi4AyEpzux0fp qm4vFuzoJlBAIz rfs8k/o0mBaNOJOcQTDyZULOsR+H2sSzJPISHUXj19Og+RJNdY34B3OM 5ZwEBF74 QXT4pP3CkGa/eJ4ATVj004dq5OWy+8bN1G0H2oDKt2U= отличие от файлов v-X.inc: 1) присутствует только версия на сервере; 2) нет флагов; 3) присутствуют все файлы. Последнее — особенно важно. Чисто теоретически, можно написать программу, которая будет выкачивать ЦЕЛИКОМ клинт с сервера обновлений! Т.е. скачали три полных списка (launcher, patcher + element — http://patch3.pwonline.ru/CPW/element/files.md5), и распаковали в нужном порядке ![]() О .cup файлах Это мини-патч, в каком-то виде. Фактически, внутри этого пака таже самая структура, что и на сервере, только там есть содержимое исключительно папки element. Опытов с ними я не проводил, но знаю, что обычно они содержат ограниченное количество изменений (т.е. внутри нет files.md5 — оно и понятно ![]() Собственно, не знаю что еще можно рассказать о структуре, так что перейдем к программистской части. Программируем? Вроде все просто. А ведь так и есть. Итак, приведу сложные моменты в реализации всего: 1) понять смешанный алгоритм упаковки; 2) понять какой алгоритм подписи и как подписывать; 3) найти в клиенте открытый ключ и заменить на свой (если не знаете почему, погуглите rsa private public key). Приведу примеры решений описанных выше проблем. Важно! Все примеры написаны на java и являются кусками нескольких классов! Возможно я скопирую не все, что нужно, но смысл работы понять можно. 1. упаковка отличается только тем, что в начало упакованного архива дописывается размер файла до упаковки ![]() Код HTML:
/** * Интерфейс упаковщика для строковых входных переменных. * @param input откуда * @param output куда * @throws Exception */ public void doPakage(String input, String output) throws Exception { doPakage(new File(input),new File(output)); } /** * Упаковщик файлов. * @param fileInput откуда * @param fileOutput куда * @throws FileNotFoundException * @throws IOException */ public void doPakage(File fileInput, File fileOutput) throws FileNotFoundException, IOException { // создадим новый файл, должно просто стереть старый. fileOutput.createNewFile(); InputStream fileInputStream = new FileInputStream(fileInput); OutputStream fileOutputStream = new FileOutputStream(fileOutput); byte[] buffer = new byte[(int)fileInput.length()]; byte[] buffer2 = new byte[(int)fileInput.length()]; ByteBuffer temp = ByteBuffer.allocate(4); temp.order(ByteOrder.LITTLE_ENDIAN); temp.putInt((int)fileInput.length()); int compressedDataLength = (int)fileInput.length(); fileInputStream.read(buffer); // упаковка файла Deflater compresser = new Deflater(Deflater.BEST_SPEED,false); compresser.setInput(buffer); compresser.finish(); compressedDataLength = compresser.deflate(buffer2); if(fileInput.length() <= compressedDataLength) buffer2 = buffer; fileOutputStream.write(temp.array()); fileOutputStream.write(buffer2, 0, compressedDataLength); fileOutputStream.flush(); fileOutputStream.close(); fileInputStream.close(); Код HTML:
private PrivateKey privateKey; // приватный ключ
private PublicKey publicKey; // паблик ключ
private BigInteger modus; // сюда мы подгрузим ключи, чтобы не приходилось каждый раз генерировать
private BigInteger privateX;
private BigInteger publicX;
/**
* Надпись перед подписью.
*/
public String signatereText = "-----BEGIN ELEMENT SIGNATURE-----\n";
/**
* Генерация ключей.
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchProviderException
*/
private void doGenerate() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);
KeyPair pair = keyGen.generateKeyPair();
privateKey = pair.getPrivate();
publicKey = pair.getPublic();
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKeySpec prks = kf.getKeySpec(privateKey, RSAPrivateKeySpec.class);
modus = prks.getModulus();
privateX = prks.getPrivateExponent();
RSAPublicKeySpec pubks = kf.getKeySpec(publicKey, RSAPublicKeySpec.class);
publicX = pubks.getPublicExponent();
}
/**
* Загрузка ключей из входных переменных.
* @param privateXL
* @param publicXL
* @param modusL
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
private void doLoad(BigInteger privateXL, BigInteger publicXL, BigInteger modusL) throws NoSuchAlgorithmException, InvalidKeySpecException {
modus = modusL;
privateX = privateXL;
publicX = publicXL;
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKeySpec new_prks = new RSAPrivateKeySpec(modus, privateX);
RSAPublicKeySpec new_pubks = new RSAPublicKeySpec(modus, publicX);
privateKey = kf.generatePrivate(new_prks);
publicKey = kf.generatePublic(new_pubks);
}
/**
* Подпись для строки what
* @param what
* @return
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws SignatureException
*/
public String doSignature(String what) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
String encryptedText;
byte[] textBytes = what.getBytes();
Signature dsa = Signature.getInstance("MD5withRSA");
dsa.initSign(privateKey);
dsa.update(textBytes);
byte[] encryptedBytes = dsa.sign();
encryptedText = CPWBase64.encodeBytes(encryptedBytes).toString();
return signatereText + encryptedText;
}
/**
* Рассчет подписи файла what и запись этой подписи в конец файла.
* @param what
* @param fos
* @return
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws SignatureException
*/
public boolean doSignature(File what, OutputStream fos) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature dsa = Signature.getInstance("MD5withRSA");
dsa.initSign(privateKey);
FileInputStream fis = new FileInputStream(what);
BufferedInputStream bufin = new BufferedInputStream(fis);
byte[] buffer = new byte[1024];
int len;
while(bufin.available() != 0) {
len = bufin.read(buffer);
dsa.update(buffer, 0, len);
}
bufin.close();
fis.close();
byte[] realSig = dsa.sign();
fos.write(signatereText.getBytes());
String signature = CPWBase64.encodeBytes(realSig);
for(int i=0; i<signature.length(); i=i+64) {
int b=i+64;
if(b > signature.length()) b = signature.length();
fos.write(new String(signature.substring(i,b)+"\n").getBytes());
}
return true;
}
![]() 3. на эмудеве я посоветовал просто найти соответствующее вхождение хекс-редактором и заменить на свое. Но мы же джедаи программирования, надо чтобы можно было тока кнопочку нажать ![]() ПомогЭ тык спасибо!) Гайд пренодлежит мне!) Просьба соблюдайте копирайты!))) ![]() |
![]() ![]() ![]() |
![]() |
![]() |
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1) | |
Опции темы | |
|
|